SpringBoot-原理解析

配置原理解析

1. 启动配置原理

配置在META-INF/spring.factories中

ApplicationContextInitializer

SpringApplicationRunListener

添加到IoC容器中

AppllicationRunner

CommandLineRunner

1.1. 启动流程

1
2
3
SpringApplication.run(BootdataApplication.class, args);

return new SpringApplication(primarySources).run(args);

1.1.1. 创建SpringApplication对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主配置类 -- 创建的主类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断是否为web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,并保存
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从类路径下找到META-INF/spring.factories配置的所有ApplicationListener,并保存
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到main方法的配置类
this.mainApplicationClass = deduceMainApplicationClass();
}

1.1.1.1. ApplicationContextInitializer

从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer

1.1.1.2. ApplicationListener

1.1.1.3. deduceMainApplicationClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

1.1.2. 运行run方法

refreshContext(context);所有组件的加载

1.1.2.1. SpringApplicationRunListeners

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//从类路径下找到META-INF/spring.factories配置的所有SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
/**
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
回调所有SpringApplicationRunListener的starting
*/
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
/**
ConfigurableEnvironment environment = getOrCreateEnvironment();
listeners.environmentPrepared(environment);
又回调SpringApplicationRunListener
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//创建图标
Banner printedBanner = printBanner(environment);
/**
switch (this.webApplicationType) {
case SERVLET:
//AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
//AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
决定创建的容器org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
*/
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文环境
/**
context.setEnvironment(environment);
applyInitializers(context); 回调所有ApplicationContextInitializer
for (ApplicationContextInitializer initializer : getInitializers()) {
initializer.initialize(context);
}
listeners.contextPrepared(context); 回调所有SpringApplicationRunListener
得到所有单例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
回调所有SpringApplicationRunListener
listeners.contextLoaded(context);
*/
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器,容器初始化,创建所有bean,tomcat等
refreshContext(context);
//空方法
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//回调所有SpringApplicationRunListener
listeners.started(context);
/**
从容器中获取所有的ApplicationRunner和CommandLineRunner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
*/
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

​ SpringApplicationRunListeners

2. 自定义starters

2.1. 自动配置

1
2
3
4
5
6
7
8
9
10
11
12
13
//指定为配置类
@Configuration
//指定条件下生成bean
@ConditionalOnxxx
//自动配置顺序
@AutoConfigureAfter
//容器中添加组件
@Bean

//xxxProperties类绑定相关配置
@ConfigurationProperties(prefix = "")
//xxxAutoConfiguration让xxxProperties生效
@EnableConfigurationProperties

META-INF/spring.factories

1
2
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

2.2. 模式

启动器只用来做依赖导入

专门写一个自动配置模块

启动器依赖自动配置

xxx-spring-boot-starter

2.3. 创建项目

新建空项目

new Module:

  • Spring-Initalizer:hello-spring-boot-starter-autoconfigurer

    仅添加spring-boot-starter

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.runaccpeted</groupId>
    <artifactId>hello-spring-boot-starter-autoconfigurer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-boot-starter-autoconfigurer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
    <java.version>1.8</java.version>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    </dependencies>
    </project>
  • Maven:hello-spring-boot-starter

    添加hello-spring-boot-starter-autoconfigurer依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.runaccpeted.starter</groupId>
    <artifactId>hello-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
    <!-- 自动配置-->
    <dependency>
    <groupId>com.runaccpeted</groupId>
    <artifactId>hello-spring-boot-starter-autoconfigurer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    </dependency>
    </dependencies>

    </project>

2.4. hello-spring-boot-starter-autoconfigurer

HelloProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.runaccpeted.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "runaccpeted.hello")
public class HelloProperties {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

HelloService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.runaccpeted.starter;

public class HelloService {

HelloProperties helloProperties;

public HelloService(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}

public String saySomething(String name) {
return helloProperties.getName()+" "+name;
}
}

HelloAutoConfiguration

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
package com.runaccpeted.starter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
@Autowired
private HelloProperties helloProperties;

public HelloProperties getHelloProperties() {
return helloProperties;
}

public void setHelloProperties(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}

@Bean
public HelloService helloService(){
HelloService service = new HelloService(helloProperties);
return service;
}
}

2.5. spring.factories

1
2
3
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.runaccpeted.starter.HelloAutoConfiguration

2.6. 安装项目

无法安装 no test – 删除hello-spring-boot-starter-autoconfigurer 中的test文件夹

2.7. 运用到项目

1
2
3
4
5
<dependency>
<groupId>com.runaccpeted.starter</groupId>
<artifactId>hello-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

TestController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.runaccpeted.controller;

import com.runaccpeted.starter.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
@Autowired
HelloService service;

@RequestMapping("/starter")
public String test(){
return service.saySomething("Hello");
}
}

application.properties

1
runaccpeted.hello.name=say:

本文结束  感谢您的阅读
  • 本文作者: Wang Ting
  • 本文链接: /zh-CN/2019/10/24/SpringBoot-原理解析/
  • 发布时间: 2019-10-24 08:49
  • 更新时间: 2022-10-24 20:42
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!