首页 > 其他分享 >springboot启动过程、自动装配原理、内置Tomcat启动原理

springboot启动过程、自动装配原理、内置Tomcat启动原理

时间:2024-06-09 10:56:03浏览次数:18  
标签:springboot Tomcat 启动 spring tomcat context new 原理

一、springboot的启动原理

springboot的启动通过在main方法的SpringApplication.run( )方法启动

@SpringBootApplication
public class ShuaApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShuaApplication.class, args);
    }
} 

首先创建SpringApplication对象,在其构造方法中做了几件事

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //1、推断并设置当前web应用类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //2、设置初始化器、监听器。从spring.factories文件中分别读取key为ApplicationContextInitializer、ApplicationListener两种接口类型的实现类。
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //3、找到main方法执行的启动类
    this.mainApplicationClass = deduceMainApplicationClass();
  }

SpringApplication.run(...)方法

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    /*
     * 1、配置属性
     * 设置系统属性 java.awt.headless,为 true 则启用headless模式
     * headless模式是应用的一种配置模式,在服务器缺少显示设备、键盘、鼠标等外设的情况下可以使用该模式
     * 比如我们使用的Linux服务器就是缺少前述的这些设备,但是又需要使用这些设备提供的能力
     */
   configureHeadlessProperty();
    /*
     * 2、获取监听器,发布应用开始启动事件
     * 通过SpringFactoriesLoader检索META-INF/spring.factories,
     * 找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
     * 之后逐个调用其starting()方法,广播SpringBoot要开始执行了
     */
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //发布ApplicationStartingEvent事件
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //3、加载配置信息:环境变量、配置文件(application.properties或application.yml)
      //发布ApplicationEnvironmentPreparedEvent事件
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      //4、创建spring上下文:ApplicationContext
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      /**
       * 5、预处理上下文
       * 逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext
       * 发布ApplicationContextInitializedEvent事件
       * load(context,sources)将启动类作为配置类,注册为BeanDefinition
       * 发布ApplicationPreparedEvent事件
      **/ 
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      /*6、刷新spring上下文:调用spring的refresh()方法
       * 包括@EnableAutoConfiguration功能,其就是通过@Import导入自动配置类,将spring.factories中的自动配置类注册为BeanDefinition
       * 另外springboot也是重写其中的onRefresh()方法创建启动Tomcat的
       */
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      //发布ApplicationStartedEvent事件
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }
​
   try {
      //发布ApplicationReadyEvent事件
      listeners.running(context);
   }
   catch (Throwable ex) {
      //发布ApplicationFailedEvent事件
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

总结

1、运行man方法:应用程序启动始于main方法的执行。首先创建一个SpringApplication实例,在其构造方法中,从spring.factires文件中加载并注册监听器(ApplicationListener)、初始化器(ApplicationInitializer)等扩展接口。

2、运行run方法:初始化spring容器并启动内置Tomcat

(1)加载配置信息:读取和解析环境变量、配置文件(application.properties或application.yml)

(2)创建spring上下文:根据应用程序类型创建相应的ApplicationContext,对于web应用,通常创建的是ServletWebServerApplicationContext。

(3)预处理上下文:将启动类作为配置类,读取并注册为BeanDefinition,这样就可以识别应用程序的配置。

(4)刷新上下文:即调用spring的refresh()方法,包括扫描解析类文件注册为BeanDefinition——>实例化bean——>依赖注入——>执行初始化方法等流程。

这里有两点要注意:

  • springboot的自动配置类是在这一步处理的,@EnableAutoConfiguration是利用@Import来实现自动装配功能(即自动加载spring.factories文件中的定义的类)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.coohua.caf.core.web.FastJsonAutoConfiguration,\
com.coohua.caf.core.base.CustomizedPropertiesBinderAutoConfiguration,\
com.coohua.caf.core.base.FrameworkConfigAutoConfiguration,\
com.coohua.caf.core.base.SpringApplicationEventListenerAutoConfiguration,\
com.coohua.caf.core.web.WebAutoConfiguration,\
com.coohua.caf.core.logging.LoggerLevelRefresherAutoConfiguration,\
com.coohua.caf.core.apollo.ApolloConfigAutoChangeAutoConfiguration
  • springboot创建和启动Tomcat是onRefresh()方法,实际是ServletWebServerApplicationContext重写了该方法。

(5)创建内置的Tomcat:ServletWebServerApplicationContext重写onRefresh()方法,在其中创建和启动Tomcat

事件机制

springboot启动过程的各个阶段发布很多事件,一个是springboot自身通过这些事件监听机制对整个流程进行一定程度的解耦,另一个就是可以供外部进行相应的扩展。

二、springboot自动装配原理

1、在主配置配类上@SpringBootApplication注解中包含@EnableAutoConfiguration注解,该注解负责自动装配功能。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(****)
public @interface SpringBootApplication {

2、@EnableAutoConfiguration利用@Import注入了处理自动装配逻辑的类AutoConfigurationImportSelector

AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

它会加载spring.factories文件中所有key为EnableAutoConfiguration的类。

注意:所有springboot通过EnableAutoConfiguration自动装配的都会在最后加载,为了确保能够方便扩展和覆盖(原理是AutoConfigurationImportSelector实现了DeferredImportSelector延迟加载)

3、spring.factories文件还有两个用处,ApplicationContextInitializer、ApplicationListener两种接口类型的实现类。这两个类是在new SpringApplication对象的构造方法中加载为实例对象的。

其中实现了ApplicationContextInitializer的类是在上面启动过程第5步的预处理上下文中执行initialize()方法,ApplicationListener则可以自己注册监听器来监听对应的事件。

org.springframework.context.ApplicationContextInitializer=\
com.coohua.caf.core.logging.Log4j2ContextInitializer

org.springframework.context.ApplicationListener=\
com.coohua.bp.config.api.spring.ConfigContainerBootListener

4、spring中@Import注解的处理时机

参考文章:https://blog.51cto.com/u_15287666/4970846#SpringImportImport_49

@Import注解的处理是利用后置处理器BeanFactoryPostProcessor,ConfigurationClassPostProcessor实现了该后置处理器,在refresh()方法中执行

 @Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
      prepareRefresh();
​
      // Tell the subclass to refresh the internal bean factory.
      //2、告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入
      //   创建bean工厂供后面使用——DefaultListableBeanFactory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
​
      // Prepare the bean factory for use in this context.
      //3、为BeanFactory配置容器特性,例如类加载器、事件处理器等
      prepareBeanFactory(beanFactory);
​
      try {
        // Allows post-processing of the bean factory in context subclasses.
        //4、为容器的某些子类指定特殊的BeanPost事件处理器
        postProcessBeanFactory(beanFactory);
​
        // Invoke factory processors registered as beans in the context.
        //5、调用所有注册的BeanFactoryPostProcessor的Bean
        invokeBeanFactoryPostProcessors(beanFactory);

ConfigurationClassPostProcessor类负责扫描和处理@Configuration注解和@Import注解。

三、springboot如何启动内置Tomcat

springboot的web容器ServletWebServerApplicationContext重写了onRefresh()方法,在其中创建并启动Tomcat。

  public class ServletWebServerApplicationContext extends GenericWebApplicationContext
    implements ConfigurableWebServerApplicationContext {
​
  @Override
  protected void onRefresh() {
    super.onRefresh();
    try {
      //创建并启动Tomcat
      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) {
      //Servlet工厂,web环境下默认是TomcatServletWebServerFactory
      ServletWebServerFactory factory = getWebServerFactory();
      //在这一步获取一个webServer,在其中创建Tomcat对象并启动
      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();
  }

TomcatServletWebServerFactory.getWebServer(...)创建Tomcat对象。

  @Override
  public WebServer getWebServer(ServletContextInitializer... initializers) {
    //创建Tomcat对象
    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);
    //在这一步启动Tomcat
    return getTomcatWebServer(tomcat);
  }

TomcatWebServer在构造方法中调用initialize(),在这个方法中启动Tomcat。

  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 {
    synchronized (this.monitor) {
      try {
        。。。。
​
        //这一步就是启动Tomcat了
        this.tomcat.start();
​
        。。。。。
      }
      catch (Exception ex) {
        stopSilently();
        throw new WebServerException("Unable to start embedded Tomcat", ex);
      }
    }
  }

 

标签:springboot,Tomcat,启动,spring,tomcat,context,new,原理
From: https://www.cnblogs.com/jing-yi/p/18239338

相关文章

  • SpringBoot架构图
    文章目录前言一、SpringBoot架构模块二、SpringBoot架构图总结前言提示:这里可以添加本文要记录的大概内容:例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。提示:以下是本篇文章正文内容,下面案......
  • k-means聚类模型的原理和应用
            k-means聚类算法是一种迭代求解的聚类分析算法,其步骤是,预将数据分为K组,然后随机选取K个对象作为初始的聚类中心;计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心;聚类中心以及分配给它们的对象就代表一个聚类;每分配一个样本,聚类......
  • Java项目-基于springboot+vue的音乐网站与分享平台 (源码+数据库+文档)​
    如需完整项目,请私信博主基于SpringBoot+Vue的音乐网站与分享平台开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven音乐网站与分享平台的主要使用者分为管理员和用户,实现功能包括管理员:首页、个人中心、用户管理、音乐资讯管理、音乐......
  • Java项目-基于springboot+vue的影城管理系统 (源码+数据库+文档)​
    如需完整项目,请私信博主基于SpringBoot+Vue的影城管理系统开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven影城管理系统的主要使用者分为管理员和用户,实现功能包括管理员:首页、个人中心、用户管理、电影类型管理、放映厅管理、电影......
  • Springboot计算机毕业设计疫情下的社区居民管理系统小程序【附源码】开题+论文+mysql+
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在新冠疫情的影响下,社区管理面临着前所未有的挑战。疫情的快速传播要求社区具备更高效、更精准的管理手段,以保障居民的健康与安全。传统的社区管理方......
  • Springboot计算机毕业设计疫情下社区资源分配小程序【附源码】开题+论文+mysql+程序+
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在全球新冠疫情的背景下,社区作为防控疫情的重要阵地,面临着巨大的挑战。随着疫情的持续,社区资源的分配和管理变得尤为重要。然而,传统的资源分配方式往......
  • 基于SpringBoot+Vue的学院党员管理系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的小型企业办公自动化系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • Springboot计算机毕业设计疫情下的“喀纳斯”出行先知小程序【附源码】开题+论文+mysq
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着全球疫情的持续演变,旅游业遭受了前所未有的冲击。作为中国西北边陲的一颗璀璨明珠,喀纳斯景区以其独特的自然风光和丰富的文化底蕴吸引了无数游客......
  • Springboot计算机毕业设计疫情下的高校食堂就餐小程序【附源码】开题+论文+mysql+程序
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在新冠疫情全球蔓延的背景下,高校作为人员密集的场所,其食堂就餐管理面临着巨大的挑战。传统的食堂就餐模式不仅容易造成人员聚集,增加病毒传播的风险,而......