SpringBoot内嵌tomcat启动过程

简介: 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服务。

相关文章
|
29天前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
263 4
|
11月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
269 1
|
8月前
|
监控 前端开发 Java
SpringBoot集成Tomcat、DispatcherServlet
通过这些配置,您可以充分利用 Spring Boot 内置的功能,快速构建和优化您的 Web 应用。
513 21
|
11月前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
145 3
|
设计模式 缓存 Java
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
|
XML Java 开发者
springboot 启动原理、启动过程、启动机制的介绍
【5月更文挑战第13天】Spring Boot 是一种基于 Java 的框架,用于创建独立的、生产级别的 Spring 应用程序。它的主要目标是简化 Spring 应用的初始搭建和开发过程,同时提供一系列大型项目常见的非功能性特征(如嵌入式服务器、安全性、度量、健康检查和外部化配置)。
1706 3
|
监控 Java Spring
深入理解Spring Boot的启动过程
深入理解Spring Boot的启动过程
|
Java 应用服务中间件 Maven
SpringBoot(六)之内嵌容器
需要用哪个容器,就把其他两个容器注释掉。(
172 0
|
存储 Java 应用服务中间件
Springboot项目打war包部署到外置tomcat容器【详解版】
该文介绍了将Spring Boot应用改为war包并在外部Tomcat中部署的步骤:1) 修改pom.xml打包方式为war;2) 排除内置Tomcat依赖;3) 创建`ServletInitializer`类继承`SpringBootServletInitializer`;4) build部分需指定`finalName`;5) 使用`mvn clean package`打包,将war包放入外部Tomcat的webapps目录,通过startup脚本启动Tomcat并访问应用。注意,应用访问路径和静态资源引用需包含war包名。
1047 0
|
6天前
|
搜索推荐 JavaScript Java
基于springboot的儿童家长教育能力提升学习系统
本系统聚焦儿童家长教育能力提升,针对家庭教育中理念混乱、时间不足、个性化服务缺失等问题,构建科学、系统、个性化的在线学习平台。融合Spring Boot、Vue等先进技术,整合优质教育资源,提供高效便捷的学习路径,助力家长掌握科学育儿方法,促进儿童全面健康发展,推动家庭和谐与社会进步。