标签:his 例子 条件 ict nconf code composite trap 用途
import org.springframework.boot.SpringApplication;
|
<1>
处,使用 @SpringBootApplication
注解,标明是 Spring Boot 应用。通过它,可以开启自动配置的功能。<2>
处,调用 SpringApplication#run(Class<?>... primarySources)
方法,启动 Spring Boot 应用。上述的代码,是我们使用 Spring Boot 时,最最最常用的代码。而本文,我们先来分析 Spring Boot 应用的启动过程。
关于
@SpringApplication
注解,我们会后面单独开文章,详细解析。
org.springframework.boot.SpringApplication
,Spring 应用启动器。正如其代码上所添加的注释,它来提供启动 Spring 应用的功能。
Class that can be used to bootstrap and launch a Spring application from a Java main method.
|
大多数情况下,我们都是使用它提供的静态方法:
// SpringApplication.java
|
SpringApplication#run(Class<?> primarySource, String... args)
方法,运行 Spring 应用。详细解析,见 「2.2 run」 。
// SpringApplication.java
|
resourceLoader
属性,资源加载器。可以暂时不理解,感兴趣的胖友,可以看看 《【死磕 Spring】—— IoC 之 Spring 统一资源加载策略》 文章。primarySources
属性,主要的 Java Config 类的数组。在文初提供的示例,就是 MVCApplication 类。webApplicationType
属性,调用 WebApplicationType#deduceFromClasspath()
方法,通过 classpath ,判断 Web 应用类型。
#createApplicationContext()
方法,将根据它的值(类型),创建不同类型的 ApplicationContext 对象,即 Spring 容器的类型不同。initializers
属性,ApplicationContextInitializer 数组。
#getSpringFactoriesInstances(Class<T> type)
方法,进行获得 ApplicationContextInitializer 类型的对象数组,详细的解析,见 「2.1.1 getSpringFactoriesInstances」 方法。initializers
属性的结果如下图:listeners
属性,ApplicationListener 数组。
#getSpringFactoriesInstances(Class<T> type)
方法,进行获得 ApplicationListener 类型的对象数组。listeners
属性的结果如下图:mainApplicationClass
属性,调用 #deduceMainApplicationClass()
方法,获得是调用了哪个 #main(String[] args)
方法,代码如下:
// SpringApplication.java
|
mainApplicationClass
属性,没有什么逻辑上的用途,主要就是用来打印下日志,说明是通过这个类启动 Spring 应用的。#getSpringFactoriesInstances(Class<T> type)
方法,获得指定类类对应的对象们。代码如下:
// SpringApplication.java
|
<1>
处,调用 SpringFactoriesLoader#loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
方法,加载指定类型对应的,在 META-INF/spring.factories
里的类名的数组。
META-INF/spring.factories
文件中,会以 KEY-VALUE 的格式,配置每个类对应的实现类们。<2>
处,调用 #createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names)
方法,创建对象们。代码如下:
// SpringApplication.java
|
<3>
处,调用 AnnotationAwareOrderComparator#sort(List<?> list)
方法,排序对象们。例如说,类上有 @Order
注解。#run(String... args)
方法,运行 Spring 应用。代码如下:
艿艿:这是一个饱满的方法,所以逻辑比较多哈。
// SpringApplication.java
|
<1>
处,创建 StopWatch 对象,并调用 StopWatch#run()
方法来启动。StopWatch 主要用于简单统计 run 启动过程的时长。<2>
处,配置 headless 属性。这个逻辑,可以无视,和 AWT 相关。<3>
处,调用 #getRunListeners(String[] args)
方法,获得 SpringApplicationRunListener 数组,并启动监听。代码如下:
// SpringApplication.java
|
listeners
变量,如下图所示:listeners
的 ApplicationListener 类型。详细的,我们在 「3. SpringApplicationRunListeners」 中,在详细解析。<4>
处,调用 #prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)
方法,加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties
和外部的属性配置。详细的,胖友先一起跳到 「2.2.1 prepareEnvironment」 中。
<5>
处,调用 #printBanner(ConfigurableEnvironment environment)
方法,打印 Spring Banner 。效果如下:
. ____ _ __ _ _
|
<6>
处,调用 #createApplicationContext()
方法,创建 Spring 容器。详细解析,见 「2.2.2 createApplicationContext」 。
<7>
处,通过 #getSpringFactoriesInstances(Class<T> type)
方法,进行获得 SpringBootExceptionReporter 类型的对象数组。SpringBootExceptionReporter ,记录启动过程中的异常信息。
exceptionReporters
属性的结果如下图:<8>
处,调用 #prepareContext(...)
方法,主要是调用所有初始化类的 #initialize(...)
方法。详细解析,见 「2.2.3 prepareContext」 。<9>
处,调用
#refreshContext(ConfigurableApplicationContext context)` 方法,启动(刷新) Spring 容器。详细解析,见 「2.2.4 refreshContext」 。<10>
处,调用 #afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args)
方法,执行 Spring 容器的初始化的后置逻辑。默认实现为空。代码如下:
// SpringApplication.java
|
<11>
处,停止 StopWatch 统计时长。
<12>
处,打印 Spring Boot 启动的时长日志。效果如下:
2019-01-28 20:42:03.338 INFO 53001 --- [ main] c.iocoder.springboot.mvc.MVCApplication : Started MVCApplication in 20.893 seconds (JVM running for 23.536)
|
<13>
处,调用 SpringApplicationRunListeners#started(ConfigurableApplicationContext context)
方法,通知 SpringApplicationRunListener 的数组,Spring 容器启动完成。
<14>
处,调用 #callRunners(ApplicationContext context, ApplicationArguments args)
方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。详细解析,见 「2.2.5 callRunners」 。
<14.1>
处,如果发生异常,则调用 #handleRunFailure(...)
方法,交给 SpringBootExceptionReporter 进行处理,并抛出 IllegalStateException 异常。<15>
处,调用 SpringApplicationRunListeners#running(ConfigurableApplicationContext context)
方法,通知 SpringApplicationRunListener 的数组,Spring 容器运行中。
<15.1>
处,如果发生异常,则调用 #handleRunFailure(...)
方法,交给 SpringBootExceptionReporter 进行处理,并抛出 IllegalStateException 异常。艿艿:这个方法,大体看下即可。
#prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)
方法,加载属性配置。代码如下:
// SpringApplication.java
|
<1>
处,调用 #getOrCreateEnvironment()
方法,创建 ConfigurableEnvironment 对象。代码如下:
// SpringApplication.java
|
webApplicationType
类型,会创建不同类型的 ConfigurableEnvironment 对象。<servletContextInitParams />
和 <servletConfigInitParams />
等配置参数。<1>
处,调用 #configureEnvironment(ConfigurableEnvironment environment, String[] args)
方法,配置 environment
变量。代码如下:
// SpringApplication.java
|
<1.1>
处,设置 environment
的 conversionService
属性。可以暂时无视。感兴趣的胖友,可以看看 《【死磕 Spring】—— 环境 & 属性:PropertySource、Environment、Profile》<1.2>
处,增加 environment
的 PropertySource 属性源。代码如下:
// SpringApplication.java
|
defaultProperties
、或者 JVM 启动参数,作为附加的 PropertySource 属性源。<1.3>
处,配置 environment
的 activeProfiles
属性。代码如下:
// SpringApplication.java
|
<2>
处,调用 SpringApplicationRunListeners#environmentPrepared(ConfigurableEnvironment environment)
方法,通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。
<3>
处,调用 #bindToSpringApplication(ConfigurableEnvironment environment)
方法,绑定 environment
到 SpringApplication 上。暂时不太知道用途。<4>
处,如果非自定义 environment
,则根据条件转换。默认情况下,isCustomEnvironment
为 false
,所以会执行这块逻辑。但是,一般情况下,返回的还是 environment
自身,所以可以无视这块逻辑先。<5>
处,调用 ConfigurationPropertySources#attach(Environment environment)
静态方法,如果有 attach 到 environment
上的 MutablePropertySources ,则添加到 environment
的 PropertySource 中。这块逻辑,也可以先无视。#createApplicationContext()
方法,创建 Spring 容器。代码如下:
// SpringApplication.java
|
webApplicationType
类型,获得对应的 ApplicationContext 对象。#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)
方法,准备 ApplicationContext 对象,主要是初始化它的一些属性。代码如下:
// SpringApplication.java
|
context
的属性做赋值,以及 ApplicationContextInitializer 的初始化。<1>
处,设置 context
的 environment
属性。<2>
处,调用 #postProcessApplicationContext(ConfigurableApplicationContext context)
方法,设置 context
的一些属性。代码如下:
// SpringApplication.java
|
<3>
处,调用 #applyInitializers(ConfigurableApplicationContext context)
方法,初始化 ApplicationContextInitializer 。代码如下:
// SpringApplication.java
|
ApplicationContextInitializer#initialize(context)
方法,进行初始化。<4>
处,调用 SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context)
方法,通知 SpringApplicationRunListener 的数组,Spring 容器准备完成。<5>
处,打印日志。效果如下:
2019-01-28 17:53:31.600 INFO 21846 --- [ main] c.iocoder.springboot.mvc.MVCApplication : Starting MVCApplication on MacBook-Pro-5.local with PID 21846 (/Users/yunai/Java/spring-boot/spring-boot-tests/spring-boot-yunai-tests/spring-boot-yunai-mvc-tests/target/classes started by yunai in /Users/yunai/Java/spring-boot)
|
<6>
处,设置 beanFactory
的属性。
<7>
处,调用 #load(ApplicationContext context, Object[] sources)
方法,加载 BeanDefinition 们。代码如下:
// SpringApplication.java
|
<1>
处,调用 #getBeanDefinitionRegistry(ApplicationContext context)
方法,创建 BeanDefinitionRegistry 对象。代码如下:
// SpringApplication.java
|
<1>
处,调用 #createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources)
方法,创建 org.springframework.boot.BeanDefinitionLoader
对象。关于它,后续的文章,详细解析。
<2>
处,设置 loader
的属性。<3>
处,调用 BeanDefinitionLoader#load()
方法,执行 BeanDefinition 加载。关于这一块,胖友感兴趣,先简单看看 《【死磕 Spring】—— IoC 之加载 BeanDefinition》 文章。<8>
处,调用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context)
方法,通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。
#refreshContext(ConfigurableApplicationContext context)
方法,启动(刷新) Spring 容器。代码如下:
// SpringApplication.java
|
<1>
处,调用 #refresh(ApplicationContext applicationContext)
方法,开启(刷新)Spring 容器。代码如下:
// SpringApplication.java
|
AbstractApplicationContext#refresh()
方法,启动(刷新)Spring 容器。
AbstractApplicationContext#refresh()
方法,胖友可以看看 《【死磕 Spring】—— ApplicationContext 相关接口架构分析》 文章。<2>
处,调用 ConfigurableApplicationContext#registerShutdownHook()
方法,注册 ShutdownHook 钩子。这个钩子,主要用于 Spring 应用的关闭时,销毁相应的 Bean 们。#callRunners(ApplicationContext context, ApplicationArguments args)
方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。代码如下:
// SpringApplication.java
|
<1>
处,获得所有 Runner 们,并进行排序。<2>
处,遍历 Runner 数组,执行逻辑。代码如下:
// SpringApplication.java
|
关于 Runner 功能的使用,可以看看 《ApplicationRunner 接口》 和 《CommandLineRunner 接口》 文档。
org.springframework.boot.SpringApplicationRunListeners
,SpringApplicationRunListener 数组的封装。代码如下:
// SpringApplicationRunListeners.java
|
org.springframework.boot.SpringApplicationRunListener
,SpringApplication 运行的监听器接口。代码如下:
// SpringApplicationRunListener.java
|
目前,SpringApplicationRunListener 的实现类,只有 EventPublishingRunListener 类。
org.springframework.boot.context.event.EventPublishingRunListener
,实现 SpringApplicationRunListener、Ordered 接口,将 SpringApplicationRunListener 监听到的事件,转换成对应的 SpringApplicationEvent 事件,发布到监听器们。
代码如下:
// EventPublishingRunListener.java |