本文以SpringBoot2.2.2.RELEASE版本為基礎(chǔ)做的分析壁却。其他版本請(qǐng)自行參考官方文檔批狱。
1. 構(gòu)建SpringBoot源碼環(huán)境
本章節(jié)主要講述通過下載Springboot源碼構(gòu)建springboot開發(fā)環(huán)境,并利用此開發(fā)環(huán)境完成單用例的開發(fā)展东。
1.1. 下載源碼
從Gihub倉庫中Clone或者下載壓縮包文件赔硫,Github地址為https://github.com/spring-projects/spring-boot,因?yàn)楫?dāng)前最新的版本為2.2.2.RELEASE盐肃,所以我在release中挑選壓縮文件下載爪膊,下載后的文件名為spring-boot-2.2.2.RELEASE.zip
按照tag权悟,找https://github.com/spring-projects/spring-boot/releases/tag/v2.2.2.RELEASE。
推盛。
下載后峦阁,解壓文件到自定義目錄,再執(zhí)行:
mvn clean install -DskipTests -Pfast
耐心等待所有依賴包下載完成耘成,再將項(xiàng)目導(dǎo)入IDEA榔昔,即可完成源碼環(huán)境構(gòu)建。
1.2. 核心父類模塊
1.2.1. spring-boot-starters
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${revision}</version>
<relativePath>../spring-boot-dependencies</relativePath>
</parent>
<artifactId>spring-boot-parent</artifactId>
<packaging>pom</packaging>
<name>Spring Boot Parent</name>
<description>Spring Boot Parent</description>
這個(gè)模塊打包POM瘪菌,它也有上級(jí)spring-boot-dependencies撒会,我看2.2.2這個(gè)版本并沒有定義starter
而是用另一個(gè)模塊spring-boot-starters來定義。詳細(xì)見下圖:
1.2.2. spring-boot-dependencies
同樣做了很多依賴包的定義控嗜,解決了我們開發(fā)過程中自己選擇依賴包版本容易沖突的問題茧彤。
1.3. 新建樣例
在spring-boot-project模塊下新建Module,命名為spring-boot-example疆栏,為了方便曾掂,一切從簡,直接構(gòu)建一個(gè)Application壁顶、Controller和yml文件珠洗。
- pom文件
<?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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>${revision}</version>
<relativePath>../spring-boot-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-example</artifactId>
<name>spring boot example</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-example</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable> <!-- Add -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 代碼
package xyz.wongs.weathertop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExamApplication {
public static void main(String[] args) {
SpringApplication.run(ExamApplication.class,args);
}
}
package xyz.wongs.weathertop;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping(value = "/index", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class IndexController {
@GetMapping(value = "/")
public List index() {
List list = new ArrayList();
list.add("這是測(cè)試");
return list;
}
}
- yml文件
server:
port: 9002
spring:
application:
name: exam-web
啟動(dòng)ExamApplication,瀏覽器打開地址若专,http://localhost:9002/index/许蓖,即可看到效果。
1.4. 小結(jié)
以上我們利用SpringBoot源碼構(gòu)建一個(gè)完整Web應(yīng)用调衰,讓大家熟悉下Springboot的目錄結(jié)構(gòu)膊爪,方便后一章節(jié)我們分析跟蹤源碼。
2. 源碼分析
2.1. 主函數(shù)入口
package xyz.wongs.weathertop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExamApplication {
public static void main(String[] args) {
SpringApplication.run(ExamApplication.class,args);
}
}
2.2. Maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
web的pom依賴嚎莉,打開源碼米酬,它已經(jīng)為我們繼承好tomcat容器和spring-web、mvc的依賴趋箩。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
2.3. 依賴分析
在當(dāng)前模塊執(zhí)行mvn dependency:tree赃额,分析下依賴包,我們可以看到叫确,這一層為我們?nèi)诤虾芏嗵迹囅胍幌拢绻@些都讓我們自己挨個(gè)去做竹勉,不可想象飞盆。
$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring boot example 2.2.2.RELEASE
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-boot-example ---
[INFO] org.springframework.boot:spring-boot-example:jar:2.2.2.RELEASE
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.2.2.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.2.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.2.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.2.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.2.2.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | | +- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | | | \- org.slf4j:slf4j-api:jar:1.7.29:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.12.1:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.12.1:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.29:compile
[INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.25:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:2.2.2.RELEASE:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.10.1:compile
[INFO] | | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.10.1:compile
[INFO] | | | \- com.fasterxml.jackson.core:jackson-core:jar:2.10.1:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.10.1:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.10.1:compile
[INFO] | | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.10.1:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.2.2.RELEASE:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.29:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.29:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.29:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-validation:jar:2.2.2.RELEASE:compile
[INFO] | | +- jakarta.validation:jakarta.validation-api:jar:2.0.1:compile
[INFO] | | \- org.hibernate.validator:hibernate-validator:jar:6.0.18.Final:compile
[INFO] | | +- org.jboss.logging:jboss-logging:jar:3.4.1.Final:compile
[INFO] | | \- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] | +- org.springframework:spring-web:jar:5.2.2.RELEASE:compile
[INFO] | | \- org.springframework:spring-beans:jar:5.2.2.RELEASE:compile
[INFO] | \- org.springframework:spring-webmvc:jar:5.2.2.RELEASE:compile
[INFO] | +- org.springframework:spring-aop:jar:5.2.2.RELEASE:compile
[INFO] | +- org.springframework:spring-context:jar:5.2.2.RELEASE:compile
[INFO] | \- org.springframework:spring-expression:jar:5.2.2.RELEASE:compile
[INFO] +- org.junit.jupiter:junit-jupiter:jar:5.5.2:test
[INFO] | +- org.junit.jupiter:junit-jupiter-api:jar:5.5.2:test
[INFO] | | +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] | | \- org.junit.platform:junit-platform-commons:jar:1.5.2:test
[INFO] | +- org.junit.jupiter:junit-jupiter-params:jar:5.5.2:test
[INFO] | \- org.junit.jupiter:junit-jupiter-engine:jar:5.5.2:test
[INFO] +- org.junit.vintage:junit-vintage-engine:jar:5.5.2:test
[INFO] | +- org.apiguardian:apiguardian-api:jar:1.1.0:test
[INFO] | +- org.junit.platform:junit-platform-engine:jar:1.5.2:test
[INFO] | \- junit:junit:jar:4.12:test
[INFO] +- org.mockito:mockito-junit-jupiter:jar:3.1.0:test
[INFO] +- org.assertj:assertj-core:jar:3.13.2:test
[INFO] +- org.mockito:mockito-core:jar:3.1.0:test
[INFO] | +- net.bytebuddy:byte-buddy:jar:1.10.4:test
[INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.10.4:test
[INFO] | \- org.objenesis:objenesis:jar:2.6:test
[INFO] +- org.hamcrest:hamcrest:jar:2.1:test
[INFO] \- org.springframework:spring-test:jar:5.2.2.RELEASE:test
[INFO] \- org.springframework:spring-core:jar:5.2.2.RELEASE:compile
[INFO] \- org.springframework:spring-jcl:jar:5.2.2.RELEASE:compile
3. 重點(diǎn)知識(shí)
3.1. 注解
SpringBoot項(xiàng)目唯一入口就是上面的Application類,這個(gè)類有一個(gè)annotation注解SpringBootApplication。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
上面的注解有幾個(gè)要重點(diǎn)說下
- 核心SpringBootConfiguration:源于Spring時(shí)代中的Configuration桨啃,都是將當(dāng)前類標(biāo)記為配置類车胡,并將當(dāng)前類以@Bean注解方法注入到Spring容器檬输。默認(rèn)允許在配置類中進(jìn)行bean間引用以及對(duì)該配置的@Bean方法的外部調(diào)用照瘾。當(dāng)proxyBeanMethods為false時(shí)候,等同于它刪除@Configuration構(gòu)造型
- 核心EnableAutoConfiguration:啟用自動(dòng)配置丧慈,幫助所有符合條件的Configuration都加載容器中析命。它的核心就是借助AutoConfigurationPackage存儲(chǔ)導(dǎo)入的基本包
,引入了AutoConfigurationImportSelector來實(shí)現(xiàn)Configuration列表的導(dǎo)出逃默。
注:AutoConfigurationImportSelector.selectImports的實(shí)現(xiàn)方式上鹃愤,在2.1和2.2之間有明顯區(qū)別,這點(diǎn)大家看的過程中需要關(guān)注下完域。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
/**
*核心就是用SpringFactoriesLoader 類的 loadFactoryNames()方法软吐,加載ClassPath路徑下META-INF/spring.factories,也就是“org.springframework.boot.autoconfigure.EnableAutoConfiguration=對(duì)應(yīng)所有的導(dǎo)出類”
*
**/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
也就是說EnableAutoConfiguration會(huì)從classpath路徑下加載所有META-INF/spring.factories吟税,將spring.factories文件中的EnableAutoConfiguration對(duì)應(yīng)的類凹耙,通過反射機(jī)制實(shí)例化標(biāo)注為@Configuration的容器類,然后即可完成依賴注入肠仪。
Target:用于描述類肖抱、接口(包括注解類型) 或enum聲明,可選值有:1异旧、FIELD-用于描述域意述;2、LOCAL_VARIABLE-用于描述局部變量吮蛹;3荤崇、METHOD-用于描述方法;4潮针、PACKAGE-用于描述包术荤;5、PARAMETER-用于描述參數(shù)
Retention:用于描述生命周期然低,可選值有:1喜每、SOURCE-只保留在源文件,當(dāng)Java文件編譯成class文件的時(shí)候雳攘,注解被遺棄带兜;2、CLASS-被保留到class文件吨灭,但jvm加載class文件時(shí)候被遺棄刚照,這是默認(rèn)的生命周期;3喧兄、RUNTIME-注解不僅被保存到class文件中无畔,jvm加載class文件之后啊楚,仍然存在
Documented:javadoc記錄,這里略過
Inherited:具備可被繼承的屬性浑彰,即子類也有同樣的注解
ComponentScan: 接觸過傳統(tǒng)spring的XML文件镀裤,我們應(yīng)該熟悉標(biāo)簽<context:component-scan>,即將特定的Bean批量注冊(cè)到Spring容器中裳擎。常用的Bean包括:@Entity环壤、@Service、@Controller诉濒、@Repository周伦、@Component。它默認(rèn)掃描SpringApplication的run方法里的Booter.class所在的包路徑下文件未荒,所以實(shí)際運(yùn)用中把該啟動(dòng)類放到包的根路徑专挪。
3.2. 構(gòu)造函數(shù)部分
main函數(shù),main中的SpringApplication.run啟動(dòng)整個(gè)web應(yīng)用片排,打開run對(duì)應(yīng)類與方法寨腔,差不多入口都是一些靜態(tài)方法。
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
上面的ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args)方法中利用構(gòu)造函數(shù)初始化SpringApplication應(yīng)用划纽,并執(zhí)行run脆侮。這就是我們要分析的入口。
3.2.1. 構(gòu)造方法
SpringApplication的核心構(gòu)造函數(shù)勇劣,主要做了兩件事來完成實(shí)例化操作:初始化上下文ApplicationContextInitializer以及ApplicationListener監(jiān)聽器靖避。
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程序(javax.servlet.Servlet和
//org.springframework.web.context.ConfigurableWebApplicationContext是否存在)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
- 以下就是ApplicationContextInitializer以及ApplicationListener初始化操作的核心部分,乍一看ClassLoader就大概清楚這是一個(gè)JDK原生類加載器比默。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
需要加載的Class路徑寫在META-INF/spring.factories中幻捏,我們打開org.springframework.boot:spring-boot和org.springframework.boo:spring-boot-autocofigure兩個(gè)jar包,看到鍵值對(duì)命咐,這里我們只看ApplicationContextInitializer和ApplicationListener部分(“\”這是換行符)篡九,獲取到實(shí)現(xiàn)類全名后交由SpringFactoriesLoader加載。
3.2.2. spring.factories機(jī)制
在上一節(jié)醋奠,我們看到spring.factories榛臼,這種擴(kuò)展機(jī)制實(shí)際上是仿照J(rèn)ava中的SPI(Service Provider Interface)擴(kuò)展機(jī)制來實(shí)現(xiàn)的,它就是在META-INF/spring.factories窜司,獲取指定接口的配置的功能沛善。而ApplicationListener就是在spring-core包中,它有兩個(gè)核心方法:
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader);
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader);
- loadFactories: 通過類加載器加載指定的類并實(shí)例化
- loadFactoryNames: 由類加載器根據(jù)自定義標(biāo)準(zhǔn)類獲取其實(shí)現(xiàn)類的實(shí)例
這樣Spring會(huì)遍歷整個(gè)ClassLoader中所有jar包下的spring.factories文件塞祈,這樣其實(shí)也為了方便開發(fā)人員自定義自己配置中spring.factories文件金刁,這樣做既不會(huì)影響到其它地方的配置,也不會(huì)被別人的配置覆蓋,這也是官方推薦的做法尤蛮。
3.3. 核心run方法簡介
初始化上下文和監(jiān)聽器之后媳友,就會(huì)進(jìn)入run方法,它負(fù)責(zé)Spring的啟動(dòng)流程产捞。
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//1醇锚、創(chuàng)建計(jì)時(shí)器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//2、設(shè)置headless模式
configureHeadlessProperty();
//3轧葛、獲取META-INF/spring.factories搂抒,這里主要是SpringApplicationRunListener監(jiān)聽
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//4、將命令行輸入的參數(shù)包裝為ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//5尿扯、準(zhǔn)備environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//6、打印Banner焰雕,也就是我們看到的Springboot的Logo衷笋,如果有自定義,則覆蓋模式
Banner printedBanner = printBanner(environment);
//7矩屁、創(chuàng)建上下文ApplicationContext
context = createApplicationContext();
//8辟宗、從META-INF/spring.factories 獲取SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//9、準(zhǔn)備上下文ApplicationContext
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//10吝秕、刷新上下文
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//11泊脐、發(fā)布started事件
listeners.started(context);
//12、執(zhí)行所有Runners
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//13烁峭、發(fā)布running事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
3.3.1. 創(chuàng)建計(jì)時(shí)器
org.springframework.util包下面一個(gè)工具類容客,主要用于對(duì)程序部分的模塊進(jìn)行計(jì)時(shí)(ms級(jí)別),一般用于同步單線程约郁。
在springboot中創(chuàng)建匿名定時(shí)器缩挑,再開啟計(jì)時(shí)。
它的好處也有無非鬓梅,大家感興趣的時(shí)候可以參看另外一篇文章作者:一個(gè)不二供置,標(biāo)題:Spring計(jì)時(shí)器StopWatch使用
3.3.2. 設(shè)置headless模式
java.awt.headless是系統(tǒng)的一種配置模式。在該模式下绽快,系統(tǒng)缺少顯示設(shè)備芥丧、鍵盤或鼠標(biāo)。尤其在面向服務(wù)器(如提供Web服務(wù))往往可能缺少前述設(shè)備坊罢,但又需要使用他們提供的功能续担,生成相應(yīng)的模擬數(shù)據(jù),以提供給客戶端(如瀏覽器所在的配有相關(guān)的艘绍、鍵盤和的主機(jī))赤拒。
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3.3.3. 獲取SpringApplicationRunListener監(jiān)聽列表
3.4. ConfigurableApplicationContext部分
這是一個(gè)接口,對(duì)應(yīng)在spring-context包中,它囊括ApplicationContext需要實(shí)現(xiàn)的所有接口挎挖,查看源碼可知它繼承ApplicationContext, Lifecycle, Closeable这敬,封裝上下文的配置和生命周期管理,在啟動(dòng)和關(guān)閉容器過程中執(zhí)行此操作蕉朵。