【深入SpringBoot 1.3.5 第一章】Boot应用的启动流程
2017-02-15 14:01
876 查看
http://blog.csdn.net/u011179993/article/details/51475732
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
App.Java作为启动类
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
* 第一个controller *
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
从上节可以看出:SpringApplication用来启动整个应用程序。
2.注册CommandLinePropertySource生成命令行参数
3.刷新application context,载入所有bean
4.运行CommandLineRunner bean
2
3
4
5
6
1
2
3
4
5
6
当然还有其他方式,不过类的内部都是根据这种方式转变而来。
通过实例化
主要调用了SpringApplication内部的initialize(..)方法,源码如下:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
流程分析
1.设置配置类
2.推断环境类型(标准或web)
3.实例化spring.factory文件中ApplicationContextInitializer对应的类
4.实例化spring.factory文件中ApplicationListener对应的类
5.推断主类
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
综上两个方法,进行流程分析:
1.程序启动监听器
Boot程序开始启动,执行其开始监听器。这个方法真正会广播ApplicationStartedEvent事件给ApplicationListener。
执行以下监听器
【LoggingApplicationListener】
配置日志
【ClasspathLoggingApplicationListener】
debug模式则开始日志信息
【LiquibaseServiceLocatorApplicationListener】
存在Liquibase类则配置Liquibase
注意:有些监听器监听了多个事件,所以下文中还会出现。
2.创建并配置环境
根据命令行参数及其系统属性、系统环境、Servlet参数等创建环境,并配置profile等。
配置完成的环境对象如下图:
3.执行环境准备好监听器
广播ApplicationEnvironmentPreparedEvent事件。 这个事件可以用来修改环境中的属性源。
【ConfigFileApplicationListener】
这个类会加载spring.factory文件中的EnvironmentPostProcessor实例到list中,~*这是一个SpringBoot的拓展点。*
然后添加本身,因为它也是EnvironmentPostProcessor
然后依次执行这这些EnvironmentPostProcessor的postProcessEnvironment方法。如下
—SpringApplicationJsonEnvironmentPostProcessor-
把环境中spring.application.json的json值转化为MapPropertySource,并将此MapPropertySource添加到环境的属性源列表中
如果在程序启动之前添加了一个系统属性json
2
3
4
5
1
2
3
4
5
那么,此processor运行完之后,环境的属性源为:
—CloudFoundryVcapEnvironmentPostProcessor-
对CloudFoundry提供支持
—ConfigFileApplicationListener-
首先,向环境的属性源列表中添加一个Random属性源,如下
然后,从配置文件中提取相应的键值添加到名为applicationConfigurationProperties属性源中,这个属性源可以包含多个子属性源
【AnsiOutputApplicationListener】
解析环境中以下属性
spring.output.ansi.enabled 来决定是否颜色输出模式,值为枚举AnsiOutput.Enabled (DETECT, ALWAYS, NEVER)
spring.output.ansi.console-available (?测试未成功)是否控制台输出
【LoggingApplicationListener】
(?未测试)配置日志相关,输出配置相关信息
【BackgroundPreinitializer】
初始化一些信息
2
3
1
2
3
【DelegatingApplicationListener】
1
【FileEncodingApplicationListener】
1
4.打印Banner图形
感兴趣的同学可以实现Banner定制自己的Banner
或者指定banner.location,或者直接添加banner.txt。
~这里还是比较有趣的、^_^
5.创建ApplicationContext并添加上面的环境
如果没有在实例变量中添加ApplicationContext,那么根据是否是网络环境来决定创建添加以下两种ApplicationContext:
2
1
2
然后把环境设置给ApplicationContext
6.后处理 ApplicationContext
根据是否含有某些实例变量来向ApplicationContext添加:
2
3
1
2
3
7.执行ApplicationContextInitializer
初始化时添加到实例变量的ApplicationContextInitializer,依次执行:
【DelegatingApplicationContextInitializer】
这个类取得环境中“context.initializer.classes”对应的多个ApplicationContextInitializer,用“,”分割,并依次执行~这里可以进行自己拓展,来修改ApplicationContext
【ContextIdApplicationContextInitializer】
根据
来设置applicationContext的ID
【ConfigurationWarningsApplicationContextInitializer】
为applicationContext添加一个BeanDefinitionRegistryPostProcessor:ConfigurationWarningsPostProcessor,来输出警告信息
例如@ComponentScan没有写明具体扫描的包名称
【ServerPortInfoApplicationContextInitializer】
添加监听器监听EmbeddedServletContainerInitializedEvent事件,用来设置嵌入的servlet容器端口号(?)
【AutoConfigurationReportLoggingInitializer】
向ApplicationContext添加AutoConfigurationReportListener用来输出自动配置报告
8.listeners.contextPrepared(context);
把之前用到的广播器添加到ApplicationContext中,没有发布任何事件
9输出Profile日志
10注册命令行参数bean
向ApplicationContext注册name为springApplicationArguments的Bean
11 载入配置sources
使用ApplicationContext.load载入资源
12 发布给ApplicationContext监听器ApplicationPreparedEvent
执行contextLoaded
【ConfigFileApplicationListener】
【LoggingApplicationListener】
【DelegatingApplicationListener】
13 refresh(context);
刷新上下文,如果是网络环境,那么进入
AnnotationConfigEmbeddedWebApplicationContext的世界
14 运行完成
主要是运行实现ApplicationRunner和CommandLineRunner接口的类
15 程序完成监听器
【ConfigFileApplicationListener】
【DelegatingApplicationListener】
后面我们将会从这12步分析~深入理解SpringBoot和Spring的各种原理。
我们发现spring的监听器贯穿在始终,所以下一节我们将会从监听器入手
顶1
一、 快速创建一个Boot应用
使用maven
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.5.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
应用程序结构
App.Java作为启动类
@EnableAutoConfiguration @ComponentScan(basePackages = {"com.jazz.controller"}) @Configuration public class App { public static void main(String[] args) { SpringApplication application = new SpringApplication(App.class); application.run(args); } }1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
* 第一个controller *
@RestController public class FirstController { @RequestMapping("/test_1.service") public String queryTeacher() { String a="Hello World"; return a; } }1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
二、SpringApplication类概要
从上节可以看出:SpringApplication用来启动整个应用程序。主要功能
1.根据classspath创建合适的ApplicationContext2.注册CommandLinePropertySource生成命令行参数
3.刷新application context,载入所有bean
4.运行CommandLineRunner bean
使用方式
public static void main(String[] args) throws Exception { //实例化--使用当前类作为source(带有@Configuration的配置类) SpringApplication app = new SpringApplication(MyApplication.class); // 启动应用 app.run(args) }1
2
3
4
5
6
1
2
3
4
5
6
当然还有其他方式,不过类的内部都是根据这种方式转变而来。
配置(source)
Boot官方文档建议使用带有@Configuration的java类作为配置,而不是XML或其他配置方式,本系列文章都使用@Configuration。
三、SpringApplication 实例化
通过实例化 SpringApplication application = new SpringApplication(App.class);
主要调用了SpringApplication内部的initialize(..)方法,源码如下:
private void initialize(Object[] sources) { //1 if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } //2 this.webEnvironment = deduceWebEnvironment(); //3 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //4 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //5 this.mainApplicationClass = deduceMainApplicationClass(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
流程分析
1.设置配置类
2.推断环境类型(标准或web)
3.实例化spring.factory文件中ApplicationContextInitializer对应的类
4.实例化spring.factory文件中ApplicationListener对应的类
5.推断主类
四、SpringApplication 执行
SpringBoot#run(String… args)
public ConfigurableApplicationContext run(String... args) { //计时器,记录应用启动消耗时间 StopWatch stopWatch = new StopWatch(); ConfigurableApplicationContext context = null; //用来设置java.awt.headless 属性是true 还是false,是J2SE的一种模式用于在缺少显示屏、键盘 //或者鼠标时的系统配置 configureHeadlessProperty(); //实例化Boot的SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //执行所有SpringApplicationRunListener的start方法 listeners.started(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //5创建并刷新Spring上下文 context = createAndRefreshContext(listeners, applicationArguments); //6执行刷新完上下文的流程 afterRefresh(context, applicationArguments); //7执行所有SpringApplicationRunListener的finish方法。 listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, ex); throw new IllegalStateException(ex); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
createAndRefreshContext(..)方法
private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableApplicationContext context; // 5.1 创建并配置环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } if (this.bannerMode != Banner.Mode.OFF) { printBanner(environment); } //5.2 Create, load, refresh 和run ApplicationContext context = createApplicationContext(); context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } //5.3 添加boot 特殊bean context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); //5.4 载入 sources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); listeners.contextLoaded(context); //5.5 刷新 context refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } return context; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
综上两个方法,进行流程分析:
1.程序启动监听器
Boot程序开始启动,执行其开始监听器。这个方法真正会广播ApplicationStartedEvent事件给ApplicationListener。
执行以下监听器
【LoggingApplicationListener】
配置日志
【ClasspathLoggingApplicationListener】
debug模式则开始日志信息
【LiquibaseServiceLocatorApplicationListener】
存在Liquibase类则配置Liquibase
注意:有些监听器监听了多个事件,所以下文中还会出现。
2.创建并配置环境
根据命令行参数及其系统属性、系统环境、Servlet参数等创建环境,并配置profile等。
配置完成的环境对象如下图:
3.执行环境准备好监听器
广播ApplicationEnvironmentPreparedEvent事件。 这个事件可以用来修改环境中的属性源。
【ConfigFileApplicationListener】
这个类会加载spring.factory文件中的EnvironmentPostProcessor实例到list中,~*这是一个SpringBoot的拓展点。*
然后添加本身,因为它也是EnvironmentPostProcessor
然后依次执行这这些EnvironmentPostProcessor的postProcessEnvironment方法。如下
—SpringApplicationJsonEnvironmentPostProcessor-
把环境中spring.application.json的json值转化为MapPropertySource,并将此MapPropertySource添加到环境的属性源列表中
如果在程序启动之前添加了一个系统属性json
public static void main(String[] args) { System.getProperties().put("spring.application.json", "{ \"name\": \"Web\"}"); SpringApplication application = new SpringApplication(App.class); application.run(args); }1
2
3
4
5
1
2
3
4
5
那么,此processor运行完之后,环境的属性源为:
—CloudFoundryVcapEnvironmentPostProcessor-
对CloudFoundry提供支持
—ConfigFileApplicationListener-
首先,向环境的属性源列表中添加一个Random属性源,如下
然后,从配置文件中提取相应的键值添加到名为applicationConfigurationProperties属性源中,这个属性源可以包含多个子属性源
【AnsiOutputApplicationListener】
解析环境中以下属性
spring.output.ansi.enabled 来决定是否颜色输出模式,值为枚举AnsiOutput.Enabled (DETECT, ALWAYS, NEVER)
spring.output.ansi.console-available (?测试未成功)是否控制台输出
【LoggingApplicationListener】
(?未测试)配置日志相关,输出配置相关信息
【BackgroundPreinitializer】
初始化一些信息
runSafely(new MessageConverterInitializer()); runSafely(new MBeanFactoryInitializer()); runSafely(new ValidationInitializer());1
2
3
1
2
3
【DelegatingApplicationListener】
这个监听器监听所有事件,如果是ApplicationEnvironmentPreparedEvent,那么获取环境中key为context.listener.classes的监听器,后续对这些监听器广播相应的事件~拓展点1
1
【FileEncodingApplicationListener】
对spring.mandatoryFileEncoding进行处理1
1
4.打印Banner图形
感兴趣的同学可以实现Banner定制自己的Banner
或者指定banner.location,或者直接添加banner.txt。
~这里还是比较有趣的、^_^
5.创建ApplicationContext并添加上面的环境
如果没有在实例变量中添加ApplicationContext,那么根据是否是网络环境来决定创建添加以下两种ApplicationContext:
AnnotationConfigEmbeddedWebApplicationContext AnnotationConfigApplicationContext。1
2
1
2
然后把环境设置给ApplicationContext
6.后处理 ApplicationContext
根据是否含有某些实例变量来向ApplicationContext添加:
beanNameGenerator resourceLoader resourceLoader1
2
3
1
2
3
7.执行ApplicationContextInitializer
初始化时添加到实例变量的ApplicationContextInitializer,依次执行:
【DelegatingApplicationContextInitializer】
这个类取得环境中“context.initializer.classes”对应的多个ApplicationContextInitializer,用“,”分割,并依次执行~这里可以进行自己拓展,来修改ApplicationContext
【ContextIdApplicationContextInitializer】
根据
${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}}
来设置applicationContext的ID
【ConfigurationWarningsApplicationContextInitializer】
为applicationContext添加一个BeanDefinitionRegistryPostProcessor:ConfigurationWarningsPostProcessor,来输出警告信息
例如@ComponentScan没有写明具体扫描的包名称
【ServerPortInfoApplicationContextInitializer】
添加监听器监听EmbeddedServletContainerInitializedEvent事件,用来设置嵌入的servlet容器端口号(?)
【AutoConfigurationReportLoggingInitializer】
向ApplicationContext添加AutoConfigurationReportListener用来输出自动配置报告
8.listeners.contextPrepared(context);
把之前用到的广播器添加到ApplicationContext中,没有发布任何事件
9输出Profile日志
10注册命令行参数bean
向ApplicationContext注册name为springApplicationArguments的Bean
11 载入配置sources
使用ApplicationContext.load载入资源
12 发布给ApplicationContext监听器ApplicationPreparedEvent
执行contextLoaded
【ConfigFileApplicationListener】
【LoggingApplicationListener】
【DelegatingApplicationListener】
13 refresh(context);
刷新上下文,如果是网络环境,那么进入
AnnotationConfigEmbeddedWebApplicationContext的世界
14 运行完成
主要是运行实现ApplicationRunner和CommandLineRunner接口的类
15 程序完成监听器
【ConfigFileApplicationListener】
【DelegatingApplicationListener】
后面我们将会从这12步分析~深入理解SpringBoot和Spring的各种原理。
我们发现spring的监听器贯穿在始终,所以下一节我们将会从监听器入手
顶1
相关文章推荐
- 【深入SpringBoot 1.3.5 第一章】Boot应用的启动流程
- SpringBoot:深入探索内嵌tomcat启动流程
- SpringBoot应用启动流程
- spring boot应用启动原理分析
- 深入理解uboot 2016 - 基础篇(处理器启动流程分析)
- spring boot应用启动原理分析
- 深入理解uboot 2016 - 基础篇(处理器启动流程分析)
- Spring Boot的应用正常启动与关闭
- spring boot应用启动原理分析
- SpringBoot应用服务启动与安全终止
- spring boot应用启动原理分析
- spring boot应用启动原理分析
- SpringBoot应用启动过程分析
- Spring Boot启动流程详解(一)
- Spring Boot启动流程详解(一)
- Spring Boot应用启动原理分析(转)
- spring boot应用启动原理分析
- 104. Spring Boot 启动流程分析第三篇【从零开始学Spring Boot】
- 深入JVM分析spring-boot应用hibernate-validator
- spring boot 启动流程