您的位置:首页 > 编程语言 > Java开发

【深入SpringBoot 1.3.5 第一章】Boot应用的启动流程

2017-02-15 14:01 876 查看
http://blog.csdn.net/u011179993/article/details/51475732

一、 快速创建一个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创建合适的ApplicationContext 

2.注册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
resourceLoader
1
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: