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服务。

相关文章
|
7月前
|
前端开发 Java 应用服务中间件
从零手写实现 tomcat-08-tomcat 如何与 springboot 集成?
该文是一系列关于从零开始手写实现 Apache Tomcat 的教程概述。作者希望通过亲自动手实践理解 Tomcat 的核心机制。文章讨论了 Spring Boot 如何实现直接通过 `main` 方法启动,Spring 与 Tomcat 容器的集成方式,以及两者生命周期的同步原理。文中还提出了实现 Tomcat 的启发,强调在设计启动流程时确保资源的正确加载和初始化。最后提到了一个名为 mini-cat(嗅虎)的简易 Tomcat 实现项目,开源于 [GitHub](https://github.com/houbb/minicat)。
|
1月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
57 1
|
1月前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
42 3
|
3月前
|
人工智能 前端开发 Java
【Tomcat源码分析】启动过程深度解析 (二)
本文深入探讨了Tomcat启动Web应用的过程,重点解析了其加载ServletContextListener及Servlet的机制。文章从Bootstrap反射调用Catalina的start方法开始,逐步介绍了StandardServer、StandardService、StandardEngine、StandardHost、StandardContext和StandardWrapper的启动流程。每个组件通过Lifecycle接口协调启动,子容器逐层启动,直至整个服务器完全启动。此外,还详细分析了Pipeline及其Valve组件的作用,展示了Tomcat内部组件间的协作机制。
【Tomcat源码分析】启动过程深度解析 (二)
|
7月前
|
前端开发 Java 应用服务中间件
从零手写实现 tomcat-08-tomcat 如何与 springboot 集成?
本文探讨了Spring Boot如何实现像普通Java程序一样通过main方法启动,关键在于Spring Boot的自动配置、内嵌Servlet容器(如Tomcat)以及`SpringApplication`类。Spring与Tomcat集成有两种方式:独立模式和嵌入式模式,两者通过Servlet规范、Spring MVC协同工作。Spring和Tomcat的生命周期同步涉及启动、运行和关闭阶段,通过事件和监听器实现。文章鼓励读者从实现Tomcat中学习资源管理和生命周期管理。此外,推荐了Netty权威指南系列文章,并提到了一个名为mini-cat的简易Tomcat实现项目。
|
4月前
|
设计模式 缓存 Java
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
|
6月前
|
监控 Java Spring
深入理解Spring Boot的启动过程
深入理解Spring Boot的启动过程
|
6月前
|
Java 应用服务中间件 Maven
SpringBoot(六)之内嵌容器
需要用哪个容器,就把其他两个容器注释掉。(
56 0
|
7月前
|
XML Java 开发者
springboot 启动原理、启动过程、启动机制的介绍
【5月更文挑战第13天】Spring Boot 是一种基于 Java 的框架,用于创建独立的、生产级别的 Spring 应用程序。它的主要目标是简化 Spring 应用的初始搭建和开发过程,同时提供一系列大型项目常见的非功能性特征(如嵌入式服务器、安全性、度量、健康检查和外部化配置)。
501 3
|
7月前
|
存储 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包名。
482 0
下一篇
DataWorks