SpringBoot内嵌tomcat启动过程

springBoot web项目需引入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring-boot-starter-web内引入了spring-boot-starter-tomcat

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <version>2.2.5.RELEASE</version>
  <scope>compile</scope>
</dependency>

Spring-boot-starter-tomcat引入了内嵌的tomcat

<dependencies>
    <dependency>
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <version>1.3.5</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>9.0.31</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>tomcat-annotations-api</artifactId>
          <groupId>org.apache.tomcat</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-el</artifactId>
      <version>9.0.31</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-websocket</artifactId>
      <version>9.0.31</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

springboot自动装配查找spring-boot-autoconfiguration下的spring.factories有个关于web服务的自动配置ServletWebServerFactoryAutoConfiguration

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

ServletWebServerFactoryAutoConfiguration import内嵌EmbeddedTomcat,条件类配置tomcat @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
// 内嵌EmbeddedTomcat
@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);
 }

  // 条件类配置tomcat
 @Bean
 @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
 public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
   ServerProperties serverProperties) {
  return new TomcatServletWebServerFactoryCustomizer(serverProperties);
 }

 @Bean
 @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
 @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
 public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
  ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
  FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
  registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
  registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
  return registration;
 }

 /**
  * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
  * {@link ImportBeanDefinitionRegistrar} for early registration.
  */
 public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

  private ConfigurableListableBeanFactory beanFactory;

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
   if (beanFactory instanceof ConfigurableListableBeanFactory) {
    this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
   }
  }

  @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);
   }
  }

 }

}

注入EmbeddedTomcat类型是一个工厂类 TomcatServletWebServerFactory

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

 @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;
  }

 }

 /**
  * Nested configuration if Jetty is being used.
  */
 @Configuration(proxyBeanMethods = false)
 @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
 @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
 static class EmbeddedJetty {

  @Bean
  JettyServletWebServerFactory JettyServletWebServerFactory(
    ObjectProvider<JettyServerCustomizer> serverCustomizers) {
   JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
   factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
   return factory;
  }

 }

 /**
  * Nested configuration if Undertow is being used.
  */
 @Configuration(proxyBeanMethods = false)
 @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
 @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
 static class EmbeddedUndertow {

  @Bean
  UndertowServletWebServerFactory undertowServletWebServerFactory(
    ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
    ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
   UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
   factory.getDeploymentInfoCustomizers()
     .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
   factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
   return factory;
  }

 }

}

TomcatServletWebServerFactory获取WebServer 创建了一个Tomcat

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
 if (this.disableMBeanRegistry) {
  Registry.disableRegistry();
 }
 Tomcat tomcat = new Tomcat();
 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
 tomcat.setBaseDir(baseDir.getAbsolutePath());
 Connector connector = new Connector(this.protocol);
 connector.setThrowOnFailure(true);
 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);
 }
  // 配置contentext
 prepareContext(tomcat.getHost(), initializers);
 return getTomcatWebServer(tomcat);
}

TomcatWebServer 初始化

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
    // 启动tomcat
   this.tomcat.start();

   // 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
    // 启动守护线程防止main函数执行完程序退出
   startDaemonAwaitThread();
  }
  catch (Exception ex) {
   stopSilently();
   destroySilently();
   throw new WebServerException("Unable to start embedded Tomcat", ex);
  }
 }
}

在spring的启动过程SpringApplication.run中对context操作有

AnnotationConfigServletWebServerApplicationContext

// 创建了AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
// 刷新上下文 
refreshContext(context);

继承关系

AbstractApplicationContext (org.springframework.context.support)
|--GenericApplicationContext (org.springframework.context.support)
|--|--GenericWebApplicationContext (org.springframework.web.context.support)
|--|--|--ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
|--|--|--|--AnnotationConfigServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)

refreshContext调用了((AbstractApplicationContext) applicationContext).refresh();

在AbstractApplicationContext类中做了一系列操作 onRefresh方法由其子类ServletWebServerApplicationContext实现

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();

   // 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();
  }
 }
}

ServletWebServerApplicationContext覆盖父类的默认实现,从factory创建webServer并启动。

protected void onRefresh() {
 super.onRefresh();
 try {
  createWebServer();
 }
 catch (Throwable ex) {
  throw new ApplicationContextException("Unable to start web server", ex);
 }
}
private void createWebServer() {
 WebServer webServer = this.webServer;
 ServletContext servletContext = getServletContext();
 if (webServer == null && servletContext == null) {
   // 获取注册的bean 即上面 EmbeddedTomcat 中注册的factory
  ServletWebServerFactory factory = getWebServerFactory();
  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();
}

总结

springboot自动配置spring.factories配置了启动类ServletWebServerFactoryAutoConfiguration,自动配置注册了EmbeddedTomcat工厂类ServletWebServerFactory,工厂类实提供例化了webService并启动服务的方法。SpringApplication.run启动过程创建了webContext上下文实例AnnotationConfigServletWebServerApplicationContext,之后通过refresh方法调用了ServletWebServerApplicationContext的onfresh方法,通过getBean的方式获取工厂并调用工厂方法ServletWebServerFactory#getWebServer生成webServer启动tomcat服务。

上一篇:云原生生态周报 Vol. 14 | K8s CVE 修复指南


下一篇:Github Pages 静态网页建站