zoukankan      html  css  js  c++  java
  • SpringBoot源码:启动内置Tomcat和加载DispatcherServlet原理

    本文基于springboot 2.2.x分支源码。

    相关的自动配置类

    关于Servlet、web内容在org.springframework.boot.autoconfigure.web.servlet包下面:

    image-20211215184240007

    该包下面主要有四个自动配置类:DispatcherServletAutoConfigurationHttpEncodingAutoConfigurationServletWebServerFactoryAutoConfigurationWebMvcAutoConfiguration

    • DispatcherServletAutoConfiguration:该类主要做DispatcherServlet相关的自动配置
    • HttpEncodingAutoConfiguration:该类主要做http字符编码相关的配置
    • ServletWebServerFactoryAutoConfiguration:该类做的是Servlet web容器工厂相关配置
    • WebMvcAutoConfiguration:该类做的是springvc相关的配置,例如配置一些消息转换器、视图解析器、包括HandlerAdapter、HandlerMapping等相关配置

    因为我们要了解springboot启动内置tomcat以及加载DispatcherServlet的原理,就需要深入DispatcherServletAutoConfigurationServletWebServerFactoryAutoConfiguration类了。

    ServletWebServerFactoryAutoConfiguration

    先看该类的注解:

    @Configuration(proxyBeanMethods = false) //配置类
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自动配置顺序
    @ConditionalOnClass(ServletRequest.class)//类路径有ServletRequest类
    @ConditionalOnWebApplication(type = Type.SERVLET)//SERVLET的web环境
    @EnableConfigurationProperties(ServerProperties.class)//使ServerProperties生效
    //导入四个组件
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
    		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
    		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
        
            //导入ServletWebServerFactoryCustomizer组件,主要对ServletWebServerFactory做定制化配置
            @Bean
    	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    		return new ServletWebServerFactoryCustomizer(serverProperties);
    	}
    
            //导入TomcatServletWebServerFactoryCustomizer组件,主要对TomcatServletWebServerFactory做定制化配置
    	@Bean
    	@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
    			ServerProperties serverProperties) {
    		return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    	}
     	//其他方法省略   
    }
    

    先看ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar

    该类实现了ImportBeanDefinitionRegistrar,所以在ConfigurationClassPostProcessor解析配置类的时候,自动调用该接口的registerBeanDefinitions方法,

    这里的BeanPostProcessorsRegistrar在该方法中注册了两个bean的定义信息:一个是ErrorPageRegistrarBeanPostProcessor,这个暂且不用管,主要是给ErrorPageRegistry中注册错误页的。

    	public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
                    //留下主要方法,其他方法省略
    		@Override
    		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
    				BeanDefinitionRegistry registry) {
    			if (this.beanFactory == null) {
    				return;
    			}
    			registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
    					WebServerFactoryCustomizerBeanPostProcessor.class);
    			registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
    					ErrorPageRegistrarBeanPostProcessor.class);
    		}
    		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
    			if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
    				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
    				beanDefinition.setSynthetic(true);
    				registry.registerBeanDefinition(name, beanDefinition);
    			}
    		}
    	}
    

    另一个是注册的bean定义信息是WebServerFactoryCustomizerBeanPostProcessor,这个类就比较有用了,看我在代码中的注释。

    //实现了BeanPostProcessor,会拦截指定bean的创建
    public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    	private ListableBeanFactory beanFactory;
    	private List<WebServerFactoryCustomizer<?>> customizers;
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            //当创建的bean是WebServerFactory时,会调用postProcessBeforeInitialization(WebServerFactory) bean)
    		if (bean instanceof WebServerFactory) {
    			postProcessBeforeInitialization((WebServerFactory) bean);
    		}
    		return bean;
    	}
    	@SuppressWarnings("unchecked")
    	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
            //getCustomizers():拿到容器中所有注册的WebServerFactoryCustomizer组件
    		LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
    				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
                	//依次执行WebServerFactoryCustomize的customize方法,该方法主要是对WebServerFactory工厂做一些定制化的工作(配置)
    				.invoke((customizer) -> customizer.customize(webServerFactory));
    	}
    	//其他方法省略。。。
    }
    

    再回来看ServletWebServerFactoryAutoConfiguration,还导入了其他三个组件。

    • ServletWebServerFactoryConfiguration.EmbeddedTomcat

    • ServletWebServerFactoryConfiguration.EmbeddedJetty

    • ServletWebServerFactoryConfiguration.EmbeddedUndertow

    默认情况,只有EmbeddedTomcat会生效,看它的代码:导入了一个TomcatServletWebServerFactory类型的组件,传入三个ObjectProvider类型参数,都是对Connector、Context、ProtocolHandler做定制化操作的,默认情况下这三个参数都没有配置,如果想要配置,只需要自己在容器中导入TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer组件就行了。

    记得这里导入了TomcatServletWebServerFactory,后面tomcat的创建就靠它。

    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    	static class EmbeddedTomcat {
    
    		@Bean
    		TomcatServletWebServerFactory tomcatServletWebServerFactory(
    				ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
    				ObjectProvider<TomcatContextCustomizer> contextCustomizers,
    				ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
    			TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    			factory.getTomcatConnectorCustomizers()
    					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getTomcatContextCustomizers()
    					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getTomcatProtocolHandlerCustomizers()
    					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
    			return factory;
    		}
    
    	}
    

    DispatcherServletAutoConfiguration

    再看这个配置类,这里面导入了两个比较重要的组件:

    1.导入DispatcherServlet这个组件,注意这个类仅仅只是保存到IOC容器中,并没有在tomcat中初始化

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自动配置顺序
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(DispatcherServlet.class)
    @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)//在ServletWebServerFactoryAutoConfiguration自动配置之后再自动配置
    public class DispatcherServletAutoConfiguration {
    
    	/**
    	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
    	 */
    	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
    
    	/**
    	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
    	 */
    	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
    
    	@Configuration(proxyBeanMethods = false)
    	@Conditional(DefaultDispatcherServletCondition.class)
    	@ConditionalOnClass(ServletRegistration.class)
    	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
    	protected static class DispatcherServletConfiguration {
    
    		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    		public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
    			DispatcherServlet dispatcherServlet = new DispatcherServlet();
    			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
    			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
    			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
    			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
    			dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
    			return dispatcherServlet;
    		}
    		//
    	}
    
    	@Configuration(proxyBeanMethods = false)
    	@Conditional(DispatcherServletRegistrationCondition.class)
    	@ConditionalOnClass(ServletRegistration.class)
    	@EnableConfigurationProperties(WebMvcProperties.class)
    	@Import(DispatcherServletConfiguration.class)
    	protected static class DispatcherServletRegistrationConfiguration {
    
    		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
    				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
    			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
    					webMvcProperties.getServlet().getPath());
    			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
    			multipartConfig.ifAvailable(registration::setMultipartConfig);
    			return registration;
    		}
    	}
    }
    

    2.导入了DispatcherServletRegistrationBean组件,先看该类的继承树,看看它继承了哪些关键组件。

    image-20211216090939243

    可以观察到,该类实现了ServletContextInitializer接口,再看接口上的英文注释。

    Interface used to configure a Servlet 3.0+ {@link ServletContext context}

    programmatically. Unlike {@link WebApplicationInitializer}, classes that implement this

    interface (and do not implement {@link WebApplicationInitializer}) will not be

    detected by {@link SpringServletContainerInitializer} and hence will not be

    automatically bootstrapped by the Servlet container.

    This interface is designed to act in a similar way to

    {@link ServletContainerInitializer}, but have a lifecycle that's managed by Spring and

    not the Servlet container.

    For configuration examples see {@link WebApplicationInitializer}.

    大致含义:这个接口不像WebApplicationInitializer一样被SpringServletContainerInitializer自动探测,因此不会自动被servlet容器启动。但是它仍有着被spring管理的生命周期。

    断点调试

    onRefresh

    springboot启动tomcat时候,日志会打印tomcat启动的日志。我们只要在springboot 的run方法上打上断点,不难找到tomcat启动在哪里。

    SpringApplication类的run方法中,这里调用了refreshContext,这一步是刷新ioc容器,这里断点一过,tomcat启动日志就打印了,所以tomcat就在这里面。

    image-20211216092442970

    走到经典的容器刷新方法,断点一走过onRefresh方法,tomcat启动日志就打印了,所以这里就是tomcat启动的地方。

    image-20211216092717329

    createWebServer

    进入onRefresh方法,先调用父类的onRefresh方法,然后调用createWebServer方法:

    	@Override
    	protected void onRefresh() {
    		super.onRefresh();
    		try {
    			createWebServer();
    		}
    		catch (Throwable ex) {
    			throw new ApplicationContextException("Unable to start web server", ex);
    		}
    	}
    

    再看createWebServer方法:

    	private void createWebServer() {
                    //默认情况下,this.webServer和servletContext都为null
    		WebServer webServer = this.webServer;
    		ServletContext servletContext = getServletContext();
    		if (webServer == null && servletContext == null) {
                            //获取WebServerFactory
    			ServletWebServerFactory factory = getWebServerFactory();
                            //从WebServerFactory中获取webServer
    			this.webServer = factory.getWebServer(getSelfInitializer());
    		}
    		else if (servletContext != null) {
    			try {
    				getSelfInitializer().onStartup(servletContext);
    			}
    			catch (ServletException ex) {
    				throw new ApplicationContextException("Cannot initialize servlet context", ex);
    			}
    		}
                    //初始化相关属性
    		initPropertySources();
    	}
    

    那么重点就是ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());

    getWebServerFactory()

    先看这个getWebServerFactory()方法:

    	protected ServletWebServerFactory getWebServerFactory() {
    		// Use bean names so that we don't consider the hierarchy
    		String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    		if (beanNames.length == 0) {
    			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
    					+ "ServletWebServerFactory bean.");
    		}
    		if (beanNames.length > 1) {
    			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
    					+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    		}
    		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    	}
    

    第一步:从容器中获取ServletWebServerFactory组件名,默认情况下,只有在ServletWebServerFactoryAutoConfiguration自动配置的TomcatServletWebServerFactory

    第二步:校验组件名,只有一个组件名的时候,不会抛出异常,如果找不到组件或者组件有多个都会抛出ApplicationContextException

    第三步:从容器中获取到ServletWebServerFactory组件,默认就是TomcatServletWebServerFactory

    getSelfInitializer()

    先看getSelfInitializer(),这个方法比较重要:内部是一个selfInitialize回调方法,并不会立马执行,这里先提一嘴,后面会说到。

    	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    		return this::selfInitialize;
    	}
    
    	//同时这个selfInitialize方法不会立刻调用,只是作为一个回调方法,当ServletContextInitializer.onStartUp调用时,才会被调用
    	private void selfInitialize(ServletContext servletContext) throws ServletException {
                    //准备WebApplicationContext,主要做一些校验和设置属性
    		prepareWebApplicationContext(servletContext);
                    //设置ApplicationScope
    		registerApplicationScope(servletContext);
                    //注册一些关于Servlet环境的组件
    		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                    //getServletContextInitializerBeans(),主要是创建了一个ServletContextInitializerBeans集合
                    //对ServletContextInitializerBeans集合遍历,是对它内部的sortedList进行遍历,里面存放了容器中的所有ServletContextInitializer
    		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                            //调用所有的ServletContextInitializer的onStartup方法
    			beans.onStartup(servletContext);
    		}
    	}
    

    对于这种写法,可能看起来有点费解,因为getSelfInitializer()的返回值是一个ServletContextInitializer,同时它也是一个函数式接口,所以等价于下面这种写法:

    	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    		return servletContext -> {
    			prepareWebApplicationContext(servletContext);
    			registerApplicationScope(servletContext);
    			WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    			for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
    				beans.onStartup(servletContext);
    			}
    		};
    	}
    

    getWebServer

    再看getWebServer方法:

    	@Override
    	public WebServer getWebServer(ServletContextInitializer... initializers) {
    		if (this.disableMBeanRegistry) {
    			Registry.disableRegistry();
    		}
                    //new了一个tomcat
    		Tomcat tomcat = new Tomcat();
    		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
                    //设置baseDir
    		tomcat.setBaseDir(baseDir.getAbsolutePath());
    		Connector connector = new Connector(this.protocol);
    		connector.setThrowOnFailure(true);
                    //添加一个connector
    		tomcat.getService().addConnector(connector);
                    //自定义connector
    		customizeConnector(connector);
    		tomcat.setConnector(connector);
    		tomcat.getHost().setAutoDeploy(false);
                    //配置引擎
    		configureEngine(tomcat.getEngine());
    		for (Connector additionalConnector : this.additionalTomcatConnectors) {
    			tomcat.getService().addConnector(additionalConnector);
    		}
                    //准备Context
    		prepareContext(tomcat.getHost(), initializers);
    		return getTomcatWebServer(tomcat);
    	}
    

    看prepareContext方法:

    	protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    		File documentRoot = getValidDocumentRoot();
                    //创建TomcatEmbeddedContext
    		TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    		if (documentRoot != null) {
    			context.setResources(new LoaderHidingResourceRoot(context));
    		}
                    //设置一些基本属性
    		context.setName(getContextPath());
    		context.setDisplayName(getDisplayName());
    		context.setPath(getContextPath());
    		File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
    		context.setDocBase(docBase.getAbsolutePath());
    		context.addLifecycleListener(new FixContextListener());
    		context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
    				: ClassUtils.getDefaultClassLoader());
    		resetDefaultLocaleMapping(context);
    		addLocaleMappings(context);
    		try {
    			context.setCreateUploadTargets(true);
    		}
    		catch (NoSuchMethodError ex) {
    			// Tomcat is < 8.5.39. Continue.
    		}
    		configureTldPatterns(context);
                    //设置WebappLoader
    		WebappLoader loader = new WebappLoader();
    		loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    		loader.setDelegate(true);
    		context.setLoader(loader);
    		if (isRegisterDefaultServlet()) {
    			addDefaultServlet(context);
    		}
    		if (shouldRegisterJspServlet()) {
    			addJspServlet(context);
    			addJasperInitializer(context);
    		}
                    //增加LifecycleListener
    		context.addLifecycleListener(new StaticResourceConfigurer(context));
                    //合并Initializers,主要是添加一些其他的ServletContextInitializer
    		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    		host.addChild(context);
                    //配置context
    		configureContext(context, initializersToUse);
    		postProcessContext(context);
    	}
    

    进入configureContext方法:

    	protected void configureContext(Context context, ServletContextInitializer[] initializers) {
                    //创建了TomcatStarter,这个实现了ServletContainerInitializer接口
                    //对servlet3.0规范熟悉的知道,tomcat在servlet容器初始化的时候,会调用所有的ServletContainerInitializer接口的onStartup方法
    		TomcatStarter starter = new TomcatStarter(initializers);
    		if (context instanceof TomcatEmbeddedContext) {
    			TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
    			embeddedContext.setStarter(starter);
    			embeddedContext.setFailCtxIfServletStartFails(true);
    		}
                    //添加到context中
    		context.addServletContainerInitializer(starter, NO_CLASSES);
    		for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
    			context.addLifecycleListener(lifecycleListener);
    		}
                    //添加tomcat阀门
    		for (Valve valve : this.contextValves) {
    			context.getPipeline().addValve(valve);
    		}
                    //错误页配置
    		for (ErrorPage errorPage : getErrorPages()) {
    			org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
    			tomcatErrorPage.setLocation(errorPage.getPath());
    			tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
    			tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
    			context.addErrorPage(tomcatErrorPage);
    		}
    		for (MimeMappings.Mapping mapping : getMimeMappings()) {
    			context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
    		}
    		configureSession(context);
    		new DisableReferenceClearingContextCustomizer().customize(context);
                    //对TomcatContext进行自定义配置
    		for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
    			customizer.customize(context);
    		}
    	}
    

    到这里,看了一大半,有人可能一脸蒙蔽,我是谁,我在哪儿?这里究竟做了哪些关键步骤?

    我来解释一下在getWebServer方法截至运行到prepareContext,springboot究竟做了哪些事情。

    • getSelfInitializer返回的是一个函数式接口,内部主要是拿到springboot中的所有ServletContextInitializer,并依次执行它的onStartup方法
    • 把这个函数式接口作为参数传入到getWebServer方法中
    • getWebServer方法主要做了以下几件事
      • 创建了一个Tomcat实例,设置了一些属性
      • 为tomcat的Host配置Context
      • 创建了一个TomcatStarter并把上面的getSelfInitializer返回的函数式接口设置到它的成员变量中,TomcatStarter实际上是ServletContainerInitializer,tomcat在servlet容器初始化的时候,会调用所有的ServletContainerInitializer接口的onStartup方法
      • 然后把TomcatStarter添加到context中

    断点继续往下走,走到getTomcatWebServer(tomcat),进去:直接new了一个TomcatWebServer,并把前面创建的tomcat传进去。

    	protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    		return new TomcatWebServer(tomcat, getPort() >= 0);
    	}
    

    断点继续往里面走:设置了属性后,直接调用initialize方法:

    	public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    		Assert.notNull(tomcat, "Tomcat Server must not be null");
    		this.tomcat = tomcat;
    		this.autoStart = autoStart;
    		initialize();
    	}
    

    initialize方法如下:终于在这一步,看到了tomcat.strart()方法,至此,tomcat终于启动。

    	private void initialize() throws WebServerException {
    		synchronized (this.monitor) {
    			try {
    				addInstanceIdToEngineName();
    
    				Context context = findContext();
    				context.addLifecycleListener((event) -> {
    					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
    						removeServiceConnectors();
    					}
    				});
    				//tomcat启动
    				this.tomcat.start();
    				rethrowDeferredStartupExceptions();
    
    				try {
    					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
    				}
    				catch (NamingException ex) {
    				}
    				startDaemonAwaitThread();
    			}
    			catch (Exception ex) {
    				stopSilently();
    				destroySilently();
    				throw new WebServerException("Unable to start embedded Tomcat", ex);
    			}
    		}
    	}
    

    TomcatStarter.onStartup

    tomcat.start()方法启动后,因为TomcatStarter实现了ServletContainerInitializer接口,在前面手动调用了context.addServletContainerInitializer(starter, NO_CLASSES);,把tomcatStart添加到context中,所以这里的TomcatStarter并不是通过SPI机制加载到tomcat中,这里与springmvc处理不一样。

    tomcat启动后,会执行ServletContainerInitializer的onStartUp方法,所以TomcatStarter会被调用:

    	@Override
    	public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
    		try {
    			for (ServletContextInitializer initializer : this.initializers) {
    				initializer.onStartup(servletContext);
    			}
    		}
    		catch (Exception ex) {
    			this.startUpException = ex;
    			// Prevent Tomcat from logging and re-throwing when we know we can
    			// deal with it in the main thread, but log for information here.
    			if (logger.isErrorEnabled()) {
    				logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
    						+ ex.getMessage());
    			}
    		}
    	}
    

    这里的this.initializers有三个,我们只要管前面传进来的getSelfInitializer()方法:这里前面提到的selfInitialize方法才会被调用

    	private void selfInitialize(ServletContext servletContext) throws ServletException {
                    //准备WebApplicationContext,主要做一些校验和设置属性
    		prepareWebApplicationContext(servletContext);
                    //设置ApplicationScope
    		registerApplicationScope(servletContext);
                    //注册一些关于Servlet环境的组件
    		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                    //getServletContextInitializerBeans(),主要是创建了一个ServletContextInitializerBeans集合
                    //对ServletContextInitializerBeans集合遍历,是对它内部的sortedList进行遍历,里面存放了容器中的所有ServletContextInitializer
    		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                            //调用所有的ServletContextInitializer的onStartup方法
    			beans.onStartup(servletContext);
    		}
    	}
    

    这里getServletContextInitializerBeans()返回了四个ServletContextInitializer:

    image-20211216104613197

    其中第一个,就是配置类中配置的DispatcherServletRegistrationBean,而springboot通过该类注册了DispatcherServlet,我们接下来来看springboot是怎么注册的。

    注册DispatcherServlet

    进入DispatcherServletRegistrationBean的onstart方法,直接进到它的父类RegistrationBean:

    	@Override
    	public final void onStartup(ServletContext servletContext) throws ServletException {
    		String description = getDescription();
    		if (!isEnabled()) {
    			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
    			return;
    		}
                    //调用register
    		register(description, servletContext);
    	}
    

    断点进入register方法

    @Override
    protected final void register(String description, ServletContext servletContext) {
        //执行addRegistration
       D registration = addRegistration(description, servletContext);
       if (registration == null) {
          logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
          return;
       }
        //调用configure
       configure(registration);
    }
    

    进入addRegistration,终于走进DispatcherServletRegistrationBean类中:直接调用servlet原生方法

    	@Override
    	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
    		String name = getServletName();
                    //添加servlet
    		return servletContext.addServlet(name, this.servlet);
    	}
    

    这里this.servlet也就是DispatcherServlet,是在前面DispatcherServletAutoConfiguration 自动配置类中传入到DispatcherServletRegistrationBean类中的。

    方法出来后,走进configure方法:这里会给ServletRegistration.Dynamic设置一些属性,这也是javax.servlet的原生方法。

    	@Override
    	protected void configure(ServletRegistration.Dynamic registration) {
    		super.configure(registration);
    		String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
    		if (urlMapping.length == 0 && this.alwaysMapUrl) {
    			urlMapping = DEFAULT_MAPPINGS;
    		}
    		if (!ObjectUtils.isEmpty(urlMapping)) {
    			registration.addMapping(urlMapping);
    		}
    		registration.setLoadOnStartup(this.loadOnStartup);
    		if (this.multipartConfig != null) {
    			registration.setMultipartConfig(this.multipartConfig);
    		}
    	}
    

    至此,tomcat启动了,DispatcherServlet注册到tomcat中,同时RegistrationBean也会注册javax原生的监听器和过滤器,具体实现是ServletListenerRegistrationBeanFilterRegistrationBean,这里不再详细讲解。

    流程图

    流程图如下:

    springboot启动tomcat原理

  • 相关阅读:
    Educational Codeforces Round 10 C. Foe Pairs 水题
    Educational Codeforces Round 10 B. z-sort 构造
    CDOJ 1048 Bob's vector 三分
    Educational Codeforces Round 10 A. Gabriel and Caterpillar 模拟
    第14届电子科大初赛民间盗版部分题目题解
    HDU 5654 xiaoxin and his watermelon candy 离线树状数组 区间不同数的个数
    HDU 5653 Bomber Man wants to bomb an Array. dp
    HDU 5652 India and China Origins 二分+并查集
    HDU 5651 xiaoxin juju needs help 数学
    HDU 5650 so easy 数学
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/15697566.html
Copyright © 2011-2022 走看看