淺談SpringBoot啟動(dòng)那些事兒

本文以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

Springboot源碼倉庫截圖

按照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)建。

導(dǎo)入IDEA目錄結(jié)構(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ì)見下圖:

starter定義

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)出逃默。
EnableAutoConfiguration類圖

注: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-bootorg.springframework.boo:spring-boot-autocofigure兩個(gè)jar包,看到鍵值對(duì)命咐,這里我們只看ApplicationContextInitializerApplicationListener部分(“\”這是換行符)篡九,獲取到實(shí)現(xiàn)類全名后交由SpringFactoriesLoader加載。

spring.factories截圖

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部分

ConfigurableApplicationContext層次圖

這是一個(gè)接口,對(duì)應(yīng)在spring-context包中,它囊括ApplicationContext需要實(shí)現(xiàn)的所有接口挎挖,查看源碼可知它繼承ApplicationContext, Lifecycle, Closeable这敬,封裝上下文的配置和生命周期管理,在啟動(dòng)和關(guān)閉容器過程中執(zhí)行此操作蕉朵。

ConfigurableApplicationContext源碼
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末崔涂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子始衅,更是在濱河造成了極大的恐慌冷蚂,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汛闸,死亡現(xiàn)場(chǎng)離奇詭異蝙茶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诸老,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門隆夯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人别伏,你說我怎么就攤上這事蹄衷。” “怎么了厘肮?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵愧口,是天一觀的道長。 經(jīng)常有香客問我类茂,道長耍属,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任大咱,我火速辦了婚禮恬涧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碴巾。我一直安慰自己溯捆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布厦瓢。 她就那樣靜靜地躺著提揍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪煮仇。 梳的紋絲不亂的頭發(fā)上劳跃,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音浙垫,去河邊找鬼刨仑。 笑死郑诺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杉武。 我是一名探鬼主播辙诞,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼轻抱!你這毒婦竟也來了飞涂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤祈搜,失蹤者是張志新(化名)和其女友劉穎较店,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體容燕,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梁呈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缰趋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捧杉。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秘血,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情评甜,我是刑警寧澤灰粮,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站忍坷,受9級(jí)特大地震影響粘舟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜佩研,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一柑肴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旬薯,春花似錦晰骑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骤公,卻和暖如春抚官,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阶捆。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工凌节, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钦听,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓倍奢,卻偏偏與公主長得像朴上,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娱挨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容