springboot的启动流程主要包括以下部分:
一、SpringBootApplication启动类
启动类的常见写法
@SpringBootApplication public class SpringmvcApplication { public static void main(String[] args) { SpringApplication.run(SpringmvcApplication.class, args); } }
需要重点关注的是@SpringBootApplication注解和SpringApplication.run(SpringmvcApplication.class, args)方法。
二、SpringApplication.run方法
1.实例化SpringApplication
SpringApplication.run(SpringmvcApplication.class, args)调用的方法;
//启动类调的run方法 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { //调的是下面的,参数是数组的run方法 return run(new Class<?>[] { primarySource }, args); } //和上面的方法区别在于第一个参数是一个数组 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { //实际上new一个SpringApplication实例,调的是一个实例方法run() return new SpringApplication(primarySources).run(args); }
判断当前项目类型
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; //断言primarySources不能为null,如果为null,抛出异常提示 Assert.notNull(primarySources, "PrimarySources must not be null"); //启动类传入的Class this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //判断当前项目类型,有三种:NONE、SERVLET、REACTIVE this.webApplicationType = WebApplicationType.deduceFromClasspath(); //设置ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //判断主类,初始化入口类 this.mainApplicationClass = deduceMainApplicationClass(); } //判断主类 private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
设置初始化器
从类路径中META-INF/spring.factories加载对应的配置文件,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。
//设置初始化器(Initializer),最后会调用这些初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
//设置初始化器(Initializer),最后会调用这些初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } // 这里的入参type就是ApplicationContextInitializer.class private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 使用Set保存names来避免重复元素 Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根据names来进行实例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 对实例进行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } // 这里的入参type就是ApplicationContextInitializer.class private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 使用Set保存names来避免重复元素 Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根据names来进行实例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 对实例进行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
设置监听器类。 主要的方式和设置初始化类一样。
整个流程:
总结
1、构造SpringApplication对象
主要判断容器类型是否为servlet、reactive还是none
2、获取ApplicationContextInitializer对象
从META-INF/spring.factories获取容器初始化需要的初始化对象
3、获取ApplicationListener对象
从META-INF/spring.factories获取监听器的配置类
2.执行run()方法
实例化后就是执行run()方法;
主要包括以下步骤:
- 第一步:获取并启动监听器
- 第二步:根据SpringApplicationRunListeners以及参数来准备环境
- 创建Environment环境变量
- 会查询系统变量,环境变量还有启动参数
- 第三步:创建Spring容器
- 第四步:Spring容器前置处理
- 第五步:刷新容器
- 将启动类作为配置类传给run()方法,作为spring容器的配置类
- 第六步:Spring容器后置处理
- 第七步:发出结束执行的事件
- 第八步:执行Runners
- 在springboot的启动过程中会执行ApplicationRunner和CommandLineRunner类型的bean
1.获取Spring容器中的ApplicationRunner类型的Bean
2.获取Spring容器中的CommandLineRunner类型的Bean
3.执行它们的run()
会在springcontext创建之前就包environment环境变量准备好;
public ConfigurableApplicationContext run(String... args) { // 计时工具 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 第一步:获取并启动监听器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 第二步:根据SpringApplicationRunListeners以及参数来准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体 Banner printedBanner = printBanner(environment); // 第三步:创建Spring容器 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 第四步:Spring容器前置处理 prepareContext(context, environment, listeners, applicationArguments,printedBanner); // 第五步:刷新容器 refreshContext(context); // 第六步:Spring容器后置处理 afterRefresh(context, applicationArguments); // 第七步:发出结束执行的事件 listeners.started(context); // 第八步:执行Runners this.callRunners(context, applicationArguments); stopWatch.stop(); // 返回容器 return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } }
3.项目启动可选的初始化操作
- 通过ApplicationRunner和CommandLineRunner
- 通过InitializingBean注解
- 通过监听事件来处理
参考资料
- SpringBoot启动流程是怎样的?:https://juejin.cn/post/6895341123816914958#heading-8
- SpringBoot 源码解析 (二)----- Spring Boot精髓:启动流程源码分析:https://www.cnblogs.com/java-chen-hao/p/11829344.html#_label0_0
todo
- 文章细节待补充待优化