SpringBoot启动流程源码分析

SpringBoot启动流程

springboot version:2.4.5

1 程序入口

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    // 新建一个springboot应用,并调用run方法
   return new SpringApplication(primarySources).run(args);
}

SpringBoot项目启动当然也是main方法作为主程序入口

2 启动过程分析

2.1 创建SpringApplication

通过 new SpringApplication 创建

// SpringApplication应用构造器
public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 
    // 入参一般为null,暂不关注
	this.resourceLoader = resourceLoader; 
    // 断言
	Assert.notNull(primarySources, "PrimarySources must not be null"); 
    // 保存主配置类
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 
    // (1) 判断spring web应用类型 
	this.webApplicationType = WebApplicationType.deduceFromClasspath(); 
    // (2) 从spring.factories中获取BootstrapRegistryInitializer实现类并保存在bootstrapRegistryInitializers中
	this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); 
    // (3) 获取ApplicationContextInitializer(Spring应用上下文初始化器)并保存
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // (4) 获取ApplicationListener(spring应用时间监听器)并保存
    // ApplicationListener用于监听Spring发布的事件
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // (5) 推断应用引导类,属于SpringApplication构造过程的末尾动作
	this.mainApplicationClass = deduceMainApplicationClass();
}

上文(1)处

// 自动判断当前工程是什么类型的application
// 根据当前ClassLoader下基准class的存在性判断
static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) 
        && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
        && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    // 一般是servlet应用,也就是web应用
    return WebApplicationType.SERVLET;
}

上文(2)处

private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
    ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList<>();
    // 获取Bootstrapper实现类并保存
    getSpringFactoriesInstances(Bootstrapper.class).stream()
        .map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize))
        .forEach(initializers::add);
    // 通过这个getSpringFactoriesInstances方法获取BootstrapRegistryInitializer
	// 启动过程中这个方法多次用来获取spring.factories中获取配置
    initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    return initializers;
}

上文(5)处

private Class<?> deduceMainApplicationClass() {
   try {
      StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
      for (StackTraceElement stackTraceElement : stackTrace) {
         if ("main".equals(stackTraceElement.getMethodName())) {
             // 返回main方法的类
            return Class.forName(stackTraceElement.getClassName());
         }
      }
   }
   catch (ClassNotFoundException ex) {
      // Swallow and continue
   }
   return null;
}

2.2 SpringApplication的run方法

// run上面2.1中创建的SpringApplication
public ConfigurableApplicationContext run(String... args) {
   // 计时秒表
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   // (6) 创建DefaultBootstrapContext,
   // 创建过程中会调用(2)中获取到的BootstrapRegistryInitializer
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   // (7) 设置java.awt.headlerss属性,默认为true
   // java系统属性,当值为true时,键盘、鼠标等图形化界面交互方式失效
   configureHeadlessProperty();
   // (8) 获取并保存SpringApplicationRunListener(spring应用运行时监听器)
   // spring内部实现类为EventPublishingRunListener,对SpringApplication运行阶段进行事件发布
   // 发布的事件可被ApplicationListener监听
   // 小伙伴可以自己实现一把这个接口,监控一下启动阶段
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // (9) 遍历调用(8)中获取到的SpringApplicationRunListeners的starting方法
   // 表示spring应用刚启动
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      // (10) args是main方法的入参,在这里保存起来
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // (11) 准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      // 配置忽略的bean info (暂不知干嘛,不影响启动流程)
      configureIgnoreBeanInfo(environment);
      // 打印banner 
      // 1:getImageBanner -> 默认 banner.jpg banner.png banner.gif 
      // 2:getTextBanner -> 默认 banner.txt
      Banner printedBanner = printBanner(environment);
      // (12) 创建spring应用上下文(IOC容器) 
      // 根据webApplicationType创建 servlet 对应AnnotationConfigServletWebServerApplicationContext
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      // (13) Spring应用上下文(context)运行前的准备工作
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      // (14) 刷新容器 将会调用spring的容器刷新方法,进入启动阶段
      refreshContext(context);
      // (15) 暂时为空方法 Called after the context has been refreshed.
      afterRefresh(context, applicationArguments);
      // 计时秒表停止
      stopWatch.stop();
      // 打印启动日志
      // Started xxx application in xxx seconds,(JVM running for xxx )
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      // (16) 遍历调用(8)中RunListeners的started方法
      // 表示Context已启动,SpringBean已初始化完成
      listeners.started(context);
      // (17)获取并调用 Runners
      // Runners有两种分别为ApplicationRunner和CommandLineRunner
      // 两种Runner的run方法入参形式不一样,执行时机一致
      // 可用来进行一次性任务执行,可作为spring启动后的执行任务
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      // (18) 捕获启动流程中的异常
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      // (19) 遍历调用(8)中Runnerlistenner的running方法
      // 表示Spring应用正在运行,Congratulations!
      listeners.running(context);
   }
   catch (Throwable ex) {
      // (20) 捕获(19)中的异常,执行(18)一样的逻辑
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

上文(6)处

private DefaultBootstrapContext createBootstrapContext() {
// 创建BootstrapContext
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 遍历调用BootstrapRegistryInitializer的初始化方法
// 执行对bootstrapContext的初始化动作
this.bootstrapRegistryInitializers.forEach((initializer) ->initializer.initialize(bootstrapContext));
return bootstrapContext;
}

上文(8)处

private SpringApplicationRunListeners getRunListeners(String[] args) {
// 注意这个types,这里要求spring.factories中SpringApplicationRunListeners的构造方法参数为这两个类型
// 且顺序不能错,否则不能初始化Runnerlistener
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 也是从spring.factories中获取
return new SpringApplicationRunListeners(logger,
      getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
      this.applicationStartup);
}

上文(11)处

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
   DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment 
// 根据(1)中的webApplicationType创建
ConfigurableEnvironment environment = getOrCreateEnvironment();
// configure the environment 对属性源或配置文件进行细粒度控制。
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 关联environment
ConfigurationPropertySources.attach(environment);
// 调用RunnerListener的environmentPrepared
// 表示environment已经准备好了,允许你进行调整
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
// 配置active profile
configureAdditionalProfiles(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
   environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
         deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}

上文 (13) 处

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
   ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
   ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置Context的environment
context.setEnvironment(environment);
// ApplicationContext 的后置处理,默认注册了一个bean,影响后续beanName的生成
// SpringBoot注释明确指明这个可被子类覆盖实现,小伙伴们可实现一把
postProcessApplicationContext(context);
// 遍历调用(3) 中上下文初始化器的初始化方法,对context进行初始化 
applyInitializers(context);
// 遍历调用(8) 中的RunListener的contextPrepared方法
// 表示Context已准备完毕,小伙伴们可以大展身手调整一下了
listeners.contextPrepared(context);
// 发布了一个bootstrapContext close事件
bootstrapContext.close(context);
if (this.logStartupInfo) {
   logStartupInfo(context.getParent() == null);
   logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 将(10)中的启动参数对象注册为bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 注册springBootBanner的bean
if (printedBanner != null) {
   beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
   // bean的Override配置,默认为false
   ((DefaultListableBeanFactory) beanFactory)
         .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 默认false
if (this.lazyInitialization) {
   // 设置beanFactory的后置处理器
   context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// @zzyang大佬:下三行代码作用:
// 获取启动类并将启动引导类转换成一个BeanDefinition注册到ioc容器中
// 当context在refresh的时候就会扫描到这个BeanDefinition
// 然后发现这个启动引导类的注解@SpringBootApplication,完成springboot的自动装配的功能
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 遍历调用(8) 中的RunListener的contextLoaded方法
// 表示Context已装载完成,但仍未启动
listeners.contextLoaded(context);
}

上文 (17) 处

private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 从容器中获取ApplicationRunner的bean
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 从容器中获取CommandLineRunner的bean
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// runner 进行排序,按照@Order或者Order接口排序
AnnotationAwareOrderComparator.sort(runners);
// 遍历调用
for (Object runner : new LinkedHashSet<>(runners)) {
   // 这个地方有个神奇的设置,为什要将两个runner的回调入参设置的不一样,猜想是兼容
   // 思考一下这个Runner和ApplicationStartedEvent(16)中发布的事件的区别?
   if (runner instanceof ApplicationRunner) {
      callRunner((ApplicationRunner) runner, args);
   }
   if (runner instanceof CommandLineRunner) {
      callRunner((CommandLineRunner) runner, args);
   }
}
}

上文 (18) 处

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
   SpringApplicationRunListeners listeners) {
try {
   try {
      // 处理异常退出码
      handleExitCode(context, exception);
      if (listeners != null) {
         // 遍历调用(8)中Runner的failed方法
         listeners.failed(context, exception);
      }
   }
   finally {
		 // 获取spring.factories中的SpringBootExceptionReporter(异常报告器)
		 // 并调用异常分析器的reportException方法
      reportFailure(getExceptionReporters(context), exception);
      if (context != null) {
         // 关闭容器
         context.close();
      }
   }
}
catch (Exception ex) {
   logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}

3 启动阶段划分

阶段过程划分
SpringApplication构造阶段(1):推断web应用类型
(2):加载BootstrapRegistryInitializer
(3):加载ApplicationContextInitializer
(4):加载ApplicationListener
(5) :推断应用引导类
SpringApplication准备阶段(6):创建DefaultBootstrapContext
(7):设置headless值(好像不是重点)
(8):加载SpringApplicationRunListener
(9):调用RunListener的starting方法
(10):保存启动参数
(11):准备Environment
(12):创建ApplicationContext
(13):准备ApplicationContext
Spring应用上下文启动阶段(14):启动ApplicationContext
Spring应用上下文启动后阶段(15):after ApplicationContext Refresh
(16):调用RunListeners的started方法
(17):调用 Runners
(19):调用Runlistenner的running方法
SpringApplication异常(18):捕获启动流程中的异常
(20):捕获(19)中的异常

简单的注释,欢迎大哥指正补充

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Boot是一个开源的Java框架,用于构建独立的、可执行的、生产级的Spring应用程序。它提供了一个快速、简单的方式来开发和部署应用程序。而在Spring Boot的启动过程中,有以下几个主要的步骤: 1. 加载启动类:Spring Boot应用程序的启动类通常是一个带有`@SpringBootApplication`注解的Java类。在应用程序启动时,会通过`main`方法加载这个启动类。 2. 创建Spring Application对象:Spring Boot会创建一个`SpringApplication`对象,用于启动应用程序。`SpringApplication`是Spring Boot框架的核心类,它负责管理整个应用程序的生命周期。 3. 解析配置信息:在启动过程中,`SpringApplication`会解析`application.properties`或`application.yaml`文件中的配置信息,并将其加载到Spring环境中。这些配置信息可以用来配置应用程序的各个方面,如数据库连接、日志级别等。 4. 创建并配置Spring容器:Spring Boot使用Spring容器来管理应用程序中的各个Bean。在启动过程中,`SpringApplication`会根据配置信息创建并配置一个Spring容器,该容器负责加载和管理应用程序中的所有Bean。 5. 执行自定义逻辑:在Spring Boot的启动过程中,可以添加自定义的逻辑。例如,可以通过实现`CommandLineRunner`接口来在应用程序启动后执行一些初始化操作。 6. 启动应用程序:完成上述步骤后,`SpringApplication`会启动应用程序,并通过Servlet容器(如Tomcat、Jetty等)监听端口,开始接收和处理HTTP请求。 总体而言,Spring Boot启动流程是一个通过加载启动类、解析配置信息、创建和配置Spring容器的过程。通过Spring Boot的自动配置和快速启动能力,开发者可以更加方便地构建和部署Spring应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值