springBoot 启动配置原理

  • springBoot 几个重要的事件回调机制
    • 配置在 META_INF/spring.factories
      • ApplicationContextInitializer
      • SpringApplicationRunListener
    • 只需要放在 ioc 容器中
      • ApplicationRunner
      • CommanLineRunner

启动流程

一、 创建 SpringApplication 对象(1.x 版本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
initialize(sources);
private void initialize(Object[] sources) {
// 保存主配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判断当前是否一个web应用
this.webEnvironment = deduceWebEnvironment();
// 从类的路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从类路径下找到META-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}

image-20200915112939801

二、 运行 run 方法(1.x 和 2.x)

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
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();

// 获取SpringApplicationRunListeners; 从类路径下META-INF/spring.factories
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 回调所有的获取SpringApplicationApplicationRunListener.starting()方法
listeners.starting();

Collection exceptionReporters;
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
Banner printedBanner = this.printBanner(environment);

// 创建ApplicationContext; 决定创建web的ioc还是普通的ioc
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备上下文环境;将environment保存到ioc中;而且applyInitalizers();
// applyInitalizers(): 回调之前保存的所有的ApplicationContextInitalizer的initze的方法
// 回调所有的SpringApplicationRunListener的contextPrepared()
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// propareContext运行完成之后回调所有的SpringApplicationRunLitsener的contextLocaded();
// 刷新容器; ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
// 扫描,创建,加载所有组件的地方
this.refreshContext(context);
// 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
// 所有的Application先回调,CommandLineRunner在回调
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 所有的SpringApplicationRunListener回调started方法
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}

try {
listeners.running(context);
// 整个SpringBoot应用启动完成以后返回启动的ioc容器
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}

三、事件监听机制

配置在 META-INF/spring.factories

ApplicationContextInitalizer

1
2
3
4
5
6
7
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {

@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("ApplicationContextInitializer...initialize..."+configurableApplicationContext);
}
}

SpringApplicationRunListener

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
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

// 必须要有构造器
public HelloSpringApplicationRunListener(SpringApplication application, String[] args){

}

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object o = environment.getSystemProperties().get("os.name");
System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
}

@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextPrepared...");
}

@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextLoaded...");
}

@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...starting...");
}

@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener...finished...");
}
}

配置(META-INFO/spring.factories)

1
2
3
4
5
org.springframework.context.ApplicationContextInitializer=\
com.oy.springboot.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.oy.springboot.listener.HelloSpringApplicationRunListener

image-20200915152907559

只需要放在 ioc 容器中

ApplicationRunner

1
2
3
4
5
6
7
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run....");
}
}

CommandLineRunner

1
2
3
4
5
6
7
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
}
}

测试

image-20200915153219630

自定义 starter

starter:

​ 1.编写自动配置

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter // 指定自动配置类的顺序
@Bean // 给容器中添加组件

@ConfigurationPropertie // 结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties // 让xxxProperties生效加入到容器中

自动配置类要能加载
将需要启动就能加载的自动配置类,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  1. 模式:
  • 启动器只用来做依赖导入:

  • 专门来写一个自动配置模块;

  • 启动器依赖自动配置;别人只需要引入启动器(starter)

    eg: mybatis-spring-boot-starter; 自定义启动器名-spring-boot-starter

演示步骤(参考):

  1. 项目结构

image-20200915170747588

  1. 先创建一个空项目,然后 oy-spring-boot-starter(用 maven 创建)和 oy-spring-boot-starter-autoconfigurer(spring Initializr 创建)

    image-20200915171032614

image-20200915171120316

【oy-spring-boot-starter】 pom.xml 配置

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>com.oy.starter</groupId>
<artifactId>oy-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependencies>

【oy-spring-boot-starter-autoconfigurer】 pom.xml 配置

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
<?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.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.oy.starter</groupId>
<artifactId>oy-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oy-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>

【HelloProperties】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@ConfigurationProperties(prefix = "oy.hello")
public class HelloProperties {
private String prefix;
private String suffix;

public String getPrefix() {
return prefix;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}

public String getSuffix() {
return suffix;
}

public void setSuffix(String suffix) {
this.suffix = suffix;
}
}

【HelloService】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloService {

HelloProperties helloProperties;

public HelloProperties getHelloProperties() {
return helloProperties;
}

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

public String sayHellAtguigu(String name){
return helloProperties.getPrefix()+"-" +name + helloProperties.getSuffix();
}

【HelloServiceAutoConfiguration】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@ConditionalOnWebApplication // Web应用才生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

@Autowired
HelloProperties helloProperties;

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

【spring.factories】

1
2
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.oy.starter.HelloServiceAutoConfiguration

测试

  • **spring-boot-08-starter-test **项目结构

image-20200915173150186

【pom.xml】

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

【HelloController.java】

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class HelloController {

@Autowired
HelloService helloService;

@GetMapping("/hello")
public String hello(){
return helloService.sayHellAtguigu("haha");
}
}

image-20200915173525543