玩转springboot之springboot启动原理

启动原理

注意:使用版本为spring-boot-2.2.2.RELEASE

springboot启动的入口肯定是main方法啦,那就从main方法入口走起来看看是如何进行启动的

java 复制代码
@SpringBootApplication
public class ConsulApp {
    public static void main(String[] args) {
      	// 调用SpringApplication的静态run方法
        SpringApplication.run(ConsulApp.class,args);
    }
}

进入main方法

java 复制代码
// 这个primarySources是传入进来的启动类
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  // 先实例化SpringApplication
   return new SpringApplication(primarySources).run(args);
}

实例化SpringApplication

java 复制代码
// this(null, primarySources)
// resourceLoader是null,primarySources是传入进来的启动类
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
  // 使用set进行去重
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  // 根据classpath中是否存在org.springframework.web.reactive.DispatcherHandler来判断是否为REACTIVE
  // 根据classpath中是否存在"javax.servlet.Servlet"和"org.springframework.web.context.ConfigurableWebApplicationContext"来判断是否为SERVLET
  // web应用的类型,是None表示非web项目  SERVLET表示普通的servlet web项目  REACTIVE表示响应式的web项目
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
  // 设置应用上下文初始化器  SpringFactoriesLoader从META-INF/spring.factories加载的,获取 ApplicationContextInitializer 接口的所有配置的类路径名称,并进行实例化
 setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
  // 设置监听器 SpringFactoriesLoader从META-INF/spring.factories加载的,获取ApplicationListener接口的所有配置的类路径名称,并进行实例化
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  // 推断主启动类,通过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为 main 的栈帧,从来得到入口类的名字再返回该类
   this.mainApplicationClass = deduceMainApplicationClass();
}

执行SpringApplication实例的run方法

实例化SpringApplication之后,调用该对象的run方法

  • NO1 进行计时,记录整个过程的加载事件
  • NO2 初始化应用上下文和异常报告集合,设置headless变量
  • NO3 通过SpringFactoriesLoader加载SpringApplicationRunListener监听器,调用starting方法,表示springboot要启动了
  • NO4 创建ConfigurableEnvironment,将配置的环境绑定到spring应用中(包括PropertySource和Profile),并调用SpringApplicationRunListener监听器的environmentPrepared方法,应用的environment已经准备完毕
  • NO5 Banner打印并创建应用上下文
  • NO6 创建应用上下文,根据webApplicationType决定创建不同的上下文
  • NO7 准备应用上下文,执行初始化器ApplicationContextInitializer的initialize方法
  • NO8 刷新应用上下文
  • NO9 计时停止,调用SpringApplicationRunListener监听器的started方法,表示应用上下文已完成
  • NO10 执行所有的Runner运行器(ApplicationRunner和CommandLineRunner)
  • NO11 调用SpringApplicationRunListener监听器的running方法,表示已经开始运行了
java 复制代码
public ConfigurableApplicationContext run(String... args) {
  // NO1 
  // 创建计时监控对象,记录整个过程的加载事件
   StopWatch stopWatch = new StopWatch();
  // 启动计时监控,记录开始时间
   stopWatch.start();
  // NO2
  // 初始化应用上下文和异常报告集合
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  
  // 设置系统属性 java.awt.headless,默认true
   configureHeadlessProperty();
  // NO3
  // 创建SpringApplicationRunListeners监听器,通过SpringFactoriesLoader加载,监听器在spring.factories中SpringApplicationRunListener接口,默认是只有org.springframework.boot.context.event.EventPublishingRunListener
  // 本质是一个事件发布者
   SpringApplicationRunListeners listeners = getRunListeners(args);
  // 开始监听,表示springboot要开始启动了
  // 广播ApplicationStartingEvent事件
   listeners.starting();
   try {
     // NO4
     // 初始化默认应用参数类
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     // 加载springboot配置环境
     // configurePropertySources(environment, args);  配置PropertySource
		// configureProfiles(environment, args);  配置profiles
     // 此时广播了一个ApplicationEnvironmentPreparedEvent事件,通知事件监听者,应用的environment已经准备完毕
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
     // NO5
     // Banner打印
      Banner printedBanner = printBanner(environment);
     // NO6 创建应用上下文,根据webApplicationType应用类型的不同,创建不同的上下文,通过Class.forName的方式
     // DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"
     // DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
     // DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"
      context = createApplicationContext();
     
     // 异常报告器,在spring.factories中SpringBootExceptionReporter接口
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
     // NO7
     // 准备应用上下文
     // 给ApplicationContext设置environment
		// 遍历调用所有的ApplicationContextInitializer的 initialize()方法
    // 广播ApplicationContextInitializedEvent事件,ApplicationContext初始化事件
    // 将所有的bean加载到容器中
    // 广播ApplicationPreparedEvent事件,ApplicationContext准备事件
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
     
     // NO8
     // 刷新应用上下文,获取所有的BeanFactoryPostProcessor对容器进行一些额外操作
     // 其中对于@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean注解都是在这里处理的
     // 这里的操作就是spring中的refresh方法那一套东西
      refreshContext(context);
     // 应用上下文刷新后置处理(该方法为空方法)
      afterRefresh(context, applicationArguments);
     // 停止计时监控
      stopWatch.stop();
      if (this.logStartupInfo) {
        // 输出主类名以及时间信息
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
     // NO9
     // 广播ApplicationStartedEvent事件,表示应用上下文已完成
      listeners.started(context);
     // NO10
     // 执行Runner运行器  ApplicationRunner和CommandLineRunner实现类
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
     // NO11
     // 发布应用上下文就绪事件
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

NO7 准备应用上下文

java 复制代码
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  // 设置环境
   context.setEnvironment(environment);
  // 配置上下文的bean生成器以及资源加载器
   postProcessApplicationContext(context);
  // 上下文初始化器执行initialize方法
   applyInitializers(context);
  // 触发监听器的contextPrepared事件
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
  // 注册两个特殊的单例bean
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
  // 加载所有资源
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
  // 加载bean
   load(context, sources.toArray(new Object[0]));
  // 触发监听器的contextLoaded事件
   listeners.contextLoaded(context);
}

NO8 刷新应用上下文

这里实际调用的就是spring中的refresh方法 可参考 源码分析之上下文构建

java 复制代码
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
     // 设置beanFactory的一些属性
     // 添加后置处理器
     // 设置忽略的自动装配接口
     // 注册一些组件
      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) {
         

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

zhhll.icu/2021/框架/spr...

相关推荐
悟空码字2 天前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
皮皮林5513 天前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602736 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840827 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解7 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解7 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记7 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者8 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840828 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解8 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端