spring-boot中tomcat的启动过程

本文源码分析基于
  • spring-boot 2.1.4
  • tomcat-embed 9.0.17
废话不多说,直接进入正题
  1. 启动 springboot
    SpringApplication.run(SpringTestApplication.class, args);
  2. 进入SpringApplication.run(String... args)中的refreshContext(context)(第316行)方法
这里的contextAnnotationConfigServletWebServerApplicationContext
【spring-boot中tomcat的启动过程】我们来看下refreshContext(context)方法的源码,如下:
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); //从这里进去 if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }

  1. 进入SpringApplication#refreshContext中的refresh(context); 方法,源码如下
/** * Refresh the underlying {@link ApplicationContext}. * @param applicationContext the application context to refresh */ protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); //这一步很重要,只要之spring应用都会调用ApplicationContext的refresh方法 }

因为AnnotationConfigServletWebServerApplicationContext的父类ServletWebServerApplicationContext重写了AbstractApplicationContextrefresh方法,我们来看下:
@Override public final void refresh() throws BeansException, IllegalStateException { try { super.refresh(); } catch (RuntimeException ex) { stopAndReleaseWebServer(); throw ex; } }

AbstractApplicationContext是非常重要的一个类,几乎所有的ApplicationContext都继承了AbstractApplicationContext
这里还是调用父类AbstractApplicationContext中的refresh方法,我们来看下:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); //tomcat就是在这一步被启动的// Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); }// Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; }finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

  1. AbstractApplicationContext#refresh中的onRefresh()方法
这一步会调用ServletWebServerApplicationContextonRefresh(),因为ServletWebServerApplicationContext重写了AbstractApplicationContextonRefresh()方法。
@Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); //创建web服务器,即tomcat } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }

  1. ServletWebServerApplicationContext中的createWebServer()方法
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) {//第一次启动会进到这里 ServletWebServerFactory factory = getWebServerFactory(); //获取Server工厂(TomcatServletWebServerFactory) 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 (本文就是TomcatServletWebServerFactory)是怎么被spring生成的
TomcatServletWebServerFactory是一个tomcat工厂,从中可以获取tomcat服务器。
首先引入与tomcat自动配置类的三个自动配置类,如下
  • ServletWebServerFactoryConfiguration
  • ServletWebServerFactoryAutoConfiguration
  • EmbeddedWebServerFactoryCustomizerAutoConfiguration
看下源码:
ServletWebServerFactoryConfiguration
@Configuration class ServletWebServerFactoryConfiguration {@Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = https://www.it610.com/article/ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { //定义TomcatServletWebServerFactory的bean definition @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); }} ... ... ... }

  • 定义TomcatServletWebServerFactory的bean definition。
ServletWebServerFactoryAutoConfiguration
@Configuration @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); }@Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } ... ... ... }

EmbeddedWebServerFactoryCustomizerAutoConfiguration
@Configuration @ConditionalOnWebApplication @EnableConfigurationProperties(ServerProperties.class) public class EmbeddedWebServerFactoryCustomizerAutoConfiguration { /** * Nested configuration if Tomcat is being used. */ @Configuration @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) public static class TomcatWebServerFactoryCustomizerConfiguration {@Bean public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new TomcatWebServerFactoryCustomizer(environment, serverProperties); }} ... ... ... }

  • ServletWebServerFactoryCustomizer 主要配置通用的 servlet 的信息,包含端口、上下文路径、应用名、Session配置、Servlet 携带的初始变量等等
  • TomcatServletWebServerFactoryCustomizer 配置tomcat的额外信息,redirectContextRoot(是否在请求根上下文时转发,true则转发路径为/demoWeb/)和useRelativeRedirects(是否使用相对路径)等路径跳转问题处理
  • TomcatWebServerFactoryCustomizer 配置tomcat的主要信息,包含 remoteIpValue、connector(最大/最小可接收线程、最大可接收头部大小等等)、uriEncoding、connectionTimeout、maxConnection等属性
Spring通过ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizerTomcatWebServerFactoryCustomizer三个配置类对tomcat完成配置。
那么这些配置什么时候注入到Tomcat服务器的呢?
不卖关子了,也很简单,就是通过一个bean后置处理器完成注入的,看下这个后置处理器,如下:
/** * {@link BeanPostProcessor} that applies all {@link WebServerFactoryCustomizer} beans * from the bean factory to {@link WebServerFactory} beans. * * @author Dave Syer * @author Phillip Webb * @author Stephane Nicoll * @since 2.0.0 */ public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {private ListableBeanFactory beanFactory; private List> customizers; @Override public void setBeanFactory(BeanFactory beanFactory) { Assert.isInstanceOf(ListableBeanFactory.class, beanFactory, "WebServerCustomizerBeanPostProcessor can only be used " + "with a ListableBeanFactory"); this.beanFactory = (ListableBeanFactory) beanFactory; }@Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebServerFactory) {//判断是否为WebServerFactory,TomcatServletWebServerFactory实现自WebServerFactory postProcessBeforeInitialization((WebServerFactory) bean); } return bean; }@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }@SuppressWarnings("unchecked") private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { LambdaSafe .callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory) .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class) .invoke((customizer) -> customizer.customize(webServerFactory)); //调用各个配置类中各个自定义方法,也就是完成配置 }private Collection> getCustomizers() { if (this.customizers == null) { // Look up does not include the parent context this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans()); this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE); this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers; }@SuppressWarnings({ "unchecked", "rawtypes" }) private Collection> getWebServerFactoryCustomizerBeans() { return (Collection) this.beanFactory .getBeansOfType(WebServerFactoryCustomizer.class, false, false).values(); //获取自定义tomcat自定义配置,也就是上面的`ServletWebServerFactoryCustomizer,`TomcatServletWebServerFactoryCustomizer`, `TomcatWebServerFactoryCustomizer`三个配置类(其实还有其他的,这里我们不深入分析) }}

该后置处理器实现了BeanFactoryAware说明它可以拿到一个BeanFactory,这里为什么不用自动装配呢?因为,Spring说明AOP和自动装配无法一起使用。
就讲到这里把。
这里还有个地方比较难理解,就是getSelfInitializer()方法,源码如下:
/** * Returns the {@link ServletContextInitializer} that will be used to complete the * setup of this {@link WebApplicationContext}. * @return the self initializer * @see #prepareWebApplicationContext(ServletContext) */ private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; }private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); // 这一步是获取所有的 servlet 组件 for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }

以上代码等同于以下代码:
/** * Returns the {@link ServletContextInitializer} that will be used to complete the * setup of this {@link WebApplicationContext}. * @return the self initializer * @see #prepareWebApplicationContext(ServletContext) */ 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); } } }

就是使用了匿名内部类,这个 ServletContextInitializer 也很重要,它的作用就是注册所有的 servlet 组件 (包括 Servlet,Filter,Listener)
  1. 通过ServletWebServerFactory(TomcatServletWebServerFactory)#getWebServer ()方法创建tomcat并启动
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); //这里会启动tomcat }

这里就是创建了tomcat。
  1. TomcatServletWebServerFactory#getWebServergetTomcatWebServer()方法
/** * Factory method called to create the {@link TomcatWebServer}. Subclasses can * override this method to return a different {@link TomcatWebServer} or apply * additional processing to the Tomcat server. * @param tomcat the Tomcat server. * @return a new {@link TomcatWebServer} instance */ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); }

/** * Create a new {@link TomcatWebServer} instance. * @param tomcat the underlying Tomcat server * @param autoStart if the server should be started */ public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); }private void initialize() throws WebServerException { logger.info("Tomcat initialized with port(s): " + getPortsDescription(false)); synchronized (this.monitor) { try { addInstanceIdToEngineName(); Context context = findContext(); context.addLifecycleListener((event) -> { if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) { // Remove service connectors so that protocol binding doesn't // happen when the service is started. removeServiceConnectors(); } }); // Start the server to trigger initialization listeners this.tomcat.start(); //启动tomcat// We can re-throw failure exception directly in the main thread rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } catch (NamingException ex) { // Naming is not enabled. Continue }// Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown startDaemonAwaitThread(); } catch (Exception ex) { stopSilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }

this.tomcat.start(); 启动tomcat。
TomcatStarter 实现了 ServletContainerInitializer。当服务器启动时,会调用 TomcatStarter#onStartup 进行Servlet 组件的初始化

    推荐阅读