【Spring boot】启动过程源码分析

启动过程结论

  • 推测web应用类型 。
  • spi的方式获取BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationContextInitializer对象 。
  • 通过调用栈推测出main()方法所在的类 。
  • 调用启动方法:run(String... args) 。后面的步骤在这个方法内部!
  • 触发SpringApplicationRunListener的starting() 。
  • 创建Environment对象 。
  • 触发SpringApplicationRunListener的environmentPrepared() 。
  • 打印Banner 。
  • 创建Spring容器对象(ApplicationContext) 。
  • 利用ApplicationContextInitializer初始化Spring容器对象 。
  • 触发SpringApplicationRunListener的contextPrepared() 。
  • 调用DefaultBootstrapContext对象的close() 。
  • 将启动类作为配置类注册到Spring容器中(load()方法) 。
  • 触发SpringApplicationRunListener的contextLoaded() 。
  • 刷新Spring容器 。
  • 触发SpringApplicationRunListener的started() 。
  • 调用ApplicationRunner和CommandLineRunner 。
  • 触发SpringApplicationRunListener的ready() 。
  • 上述过程抛异常了就触发SpringApplicationRunListener的failed() 。
入口位置
  • 第一步构造对象:new SpringApplication(primarySources)
  • 第二步调用SpringApplication.run(String... args)
// 我们自己写的main方法@SpringBootApplicationpublic class ZfcqApp {public static void main(String[] args) {SpringApplication.run(ZfcqApp.class, args);}}/** * 他会调用SpringApplication的run方法 * primarySource:我们传入的类的class * args:我们传入的参数,一般是启动的时候-D制定的参数 */public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}/** * 继续看内部调用的run方法 */public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 俩步,第一步构造对象:new SpringApplication(primarySources)// 第二步调用SpringApplication.run(String... args)// 这里我们可以在main方法中分开俩步去写,从而可以在中间设置SpringApplication对象的信息return new SpringApplication(primarySources).run(args);}构造SpringApplication源码分析/** * 调用俩个参数的构造方法 * primarySources:我们主类的class */public SpringApplication(Class<?>... primarySources) {this(null, primarySources);}/** * 俩个产生的构造方法 */public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// 赋值到全局,这里第一次传入的是nullthis.resourceLoader = resourceLoader;// 主类存在的判断Assert.notNull(primarySources, "PrimarySources must not be null");// 赋值到全局,这里一般传入的是我们的main方法的类this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 推测WEB应用的类型(NONE、SERVLET、REACTIVE)this.webApplicationType = WebApplicationType.deduceFromClasspath();// 从spring.factories中获取BootstrapRegistryInitializer对象的值this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));// 从spring.factories中获取ApplicationContextInitializer对象的值setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 从spring.factories中获取ApplicationListener对象的值 。非常重要的有一个是EnvironmentPostProcessorApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 推测出main方法所在的类!SpringApplication.run(ZfcqApp.class, args);可以传任意的类!this.mainApplicationClass = deduceMainApplicationClass();}推测web应用类型
  • REACTIVE:web应用 。
  • NONE:无Servlet,不是web应用 。
  • SERVLET:除去上面俩种的其他应用 。
static WebApplicationType deduceFromClasspath() {// 如果项目依赖中存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.REACTIVEif (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}// 如果项目依赖中不存在org.springframework.web.reactive.Dispatche#rHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.NONEfor (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}// 否则,应用类型为WebApplicationType.SERVLETreturn WebApplicationType.SERVLET;}

经验总结扩展阅读