Spring Boot 的启动
新的 Spring Boot 的文章
SpringBoot
新建一个SpringBoot工程的示例
关于启动流程
SpringBoot首先会创建
SpringBootApplication
对象初始化对象
1
2
3
4
5
6
7
8
9
10
11
12
13public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
//primarySources就是当前SpringBootApplication对象
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));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
然后执行
run
方法
Spring Boot应用的整个启动流程都封装在SpringApplication.run方法中,本质上其实就是在spring的基础之上做了封装,做了大量的扩张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
62public ConfigurableApplicationContext run(String... args) {
//启动开始停止的监听
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//声明IOC容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//是关于awt的
configureHeadlessProperty();
//通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners(从META-INF/spring.factories),通过调用starting()方法通知所有的SpringApplicationRunListeners:应用开始启动了
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared(),表示环境准备完成
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印SPRING那个大图标
Banner printedBanner = printBanner(environment);
//创建IOC容器,会判断是不是web,利用反射创建容器
context = createApplicationContext();
//创建一系列FailureAnalyzer
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文环境
//将environment保存到IOC
//还有applyInitializers(),回调之前保存的所有的ApplicationContextInitialize方法
//还要回调所有的SpringApplicationRunListener的contextPrepared()
//还有什么banner什么的
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//prepareContext完成后回调所有的SpringApplicationRunListener的contextLoaded()
//刷新容器,IOC容器初始化的过程,扫描、创建、加载所有的组件
//如果是web,tomcat也在这里创建好
refreshContext(context);
//afterRefresh:从IOC容器中获取所有的ApplicationRunner(先)和CommandLineRunner(后)进行回调
afterRefresh(context, applicationArguments);
//完成
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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);
}
//返回启动的IOC容器
return context;
}1
2
3
4private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}configureHeadlessProperty是关于awt的,给属性设值System.setProperty(),它的值来源于System.getProperty().为什么把属性从一个地方取出来,然后又设置到同一个地方?System中getProperty()有2个重载方法,但却只有一个setProperty()方法,其中getProperty()有单参和双参两方法,单参就是简单的获取属性,有就有,没有就没有,双参则聪明一点,在没有的时候会返回一个调用者指定的默认值,所以经过这样操作后,不管有没有那个属性,最终都能保证有。所以先取后设
即使没有检测到显示器,也允许其启动。对于服务器来说,是不需要显示器的,所以要这样设置几个重要的事件回调机制
- 在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
- IOC容器中
ApplicationRunner
CommandLineRunner
- 在META-INF/spring.factories
实现它们来观察SpringBoot启动顺序
四个实现
1
2
3
4
5
6
7public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
//ConfigurableApplicationContext ioc
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("MyApplicationContextInitializer initialize: " + applicationContext);
}
}
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
```java
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
public MySpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("MySpringApplicationRunListener start");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object o = environment.getSystemProperties().get("os.name");
System.out.println("SpringApplicationRunListener的environmentPrepared... os name:" + o);
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
// context.getBean("xxx")
System.out.println("SpringApplicationRunListener的contextPrepared (ioc)准备好");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener的contextLoaded context加载ok");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("MySpringApplicationRunListener started");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("MySpringApplicationRunListener running");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("MySpringApplicationRunListener failed");
}
}1
2
3
4
5
6
7
8
public class MyApplicationRunner implements ApplicationRunner {
public void run(ApplicationArguments args) throws Exception {
//args命令行参数
System.out.println("MyApplicationRunner run");
}
}记得ApplicationContextInitializer和SpringApplicationRunListener要在spring.factories声明1
2
3
4
5
6
7
public class MyCommandLineRunner implements CommandLineRunner {
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner run");
}
}还有其他两个要@Component,它们两个B在IOC中
结果一目了然!! ![结果](F:\blog\source\_posts\SpringBoot的小复习\test3.png)1
2
3
4
5# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
cn.kebabshell.springbootlearn.listener.MyApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
cn.kebabshell.springbootlearn.listener.MySpringApplicationRunListener自定义starter场景启动器
官方的有例如aop什么的分为starter和autoconfigurer
例如新建空工程,创建两个模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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>cn.kebabshell.starter</groupId>
<artifactId>kebabshell-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--引入自动配置模块-->
<dependency>
<groupId>cn.kebabshell</groupId>
<artifactId>kebabshell-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
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
```xml
<?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.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kebabshell</groupId>
<artifactId>kebabshell-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kebabshell-spring-boot-starter-autoconfigurer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--引入spring-boot-starter,所有starter的基本配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>1
autoconfigurer
创建MyProperties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//这里的prefix就是引入这个starter的项目在配置文件里面可以配置的前缀
public class MyProperties {
//自定义的一些属性
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 创建TestService
```java
public class TestService {
private MyProperties properties;
public MyProperties getProperties() {
return properties;
}
public void setProperties(MyProperties properties) {
this.properties = properties;
}
//根据别的项目调用test方法,可以进行一些操作
public String test(String str){
return "im config prefix:" + properties.getPrefix() + " suffix:" + properties.getSuffix() + " args:" + str;
}
}- 创建MyAutoConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
//Web才生效
public class MyAutoConfiguration {
private MyProperties properties;
//加入ioc
public TestService testService(){
TestService testService = new TestService();
testService.setProperties(properties);
return testService;
}
}1
2
3
4
5
6
7
- 创建spring.factories
![spring.factories](F:\blog\source\_posts\SpringBoot的小复习\test6.png)
```xml
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.kebabshell.MyAutoConfiguration- 依次构建包
新建web工程
引入自定义starter
1
2
3
4
5
6<!--自定义starter-->
<dependency>
<groupId>cn.kebabshell.starter</groupId>
<artifactId>kebabshell-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
- 创建controller就可以进行测试了
```java
@RestController
public class TestController {
@Autowired
private TestService service;
@GetMapping("/test")
public String test(){
return service.test("hello");
}
}- 结果 ![result](F:\blog\source\_posts\SpringBoot的小复习\test8.png)