SpringBoot的啟動(dòng)配置原理

一、啟動(dòng)流程

  1. 創(chuàng)建SpringApplication對(duì)象
public class SpringApplication {
 
    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 保存主配置類
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判斷當(dāng)前是否一個(gè)web應(yīng)用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();                                                            
         // 從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer拄轻;然后保存起來桨啃。
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 從類路徑下找到META-INF/spring.factories配置的所有ApplicationListener
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //從多個(gè)配置類中找到有main方法的主配置類
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
  1. 運(yùn)行run方法
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        // 獲取SpringApplicationRunListener; 從類路徑下META-INF/spring.factories獲取
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 回調(diào)所有的獲取SpringApplicationRunListener.starting()方法
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 封裝命令行參數(shù)
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 準(zhǔn)備環(huán)境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 創(chuàng)建環(huán)境完成后回調(diào)SpringApplicationRunListener.environmentPrepared();表示環(huán)境準(zhǔn)備完成
            Banner printedBanner = this.printBanner(environment);
            // 創(chuàng)建ApplicationContext忍燥;決定創(chuàng)建web的IOC還是普通的IOC
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 準(zhǔn)備上下文環(huán)境墓卦;將environment保存到IOC中,而且applyInitializers();
            // 而且applyInitializers():回調(diào)之前保存的所有ApplicationContextInitializer的initialize方法
            // 回調(diào)所有的SpringApplicationRunListener的contextPrepared();
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // prepareContext運(yùn)行完成以后回調(diào)所有的SpringApplicationRunListener的contextLoaded();
            // 掃描容器睛榄;IOC容器初始化(如果是web應(yīng)用還會(huì)創(chuàng)建嵌入的Tomcat)荣茫;
            // 掃描,創(chuàng)建场靴,加載所有組件的地方(配置類啡莉,組件,自動(dòng)配置)
            this.refreshContext(context);
            // 從IOC容器中獲取所有的ApplicationRunner和CommandLineRunner進(jìn)行回調(diào)
            // ApplicationRunner先回調(diào)旨剥,CommandLineRunner再回調(diào)
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            // 整個(gè)SpringBoot應(yīng)用啟動(dòng)完成以后返回啟動(dòng)的IOC容器
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
  1. 事件監(jiān)聽機(jī)制
    需要配置在META-INF/spring.factories中的事件監(jiān)聽器咧欣。
    ApplicationContextInitializer
public class CustomApplicationContextInitializer implements 
        ApplicationContextInitializer<ConfigurableApplicationContext> {

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

SpringApplicationRunListener

public class CustomSrpingApplicationRunListener implements SpringApplicationRunListener {
    
    // 必須有的構(gòu)造器
    public CustomSrpingApplicationRunListener(SpringApplication application,String[] args){
        
    }
    @Override
    public void starting() {
        System.out.println("SpringApplicationRunListener...starting....");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        Object o = environment.getSystemEnvironment().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..started");
    }

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

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

配置(META-INF/spring.factories)

org.springframework.context.ApplicationContextInitializer=com.desperado.demo.CustomApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=com.desperado.demo.CustomSrpingApplicationRunListener

只需要放在IOC容器中的監(jiān)聽器。
ApplicationRunner

@Component
public class CustomApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        System.out.println("ApplicationRunner ... run....");
    }
}

CommandLineRunner

@Component
public class CustomCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        System.out.println("commandLineRunner ... run ...");
    }
}

二轨帜、自定義starter

  1. 要使用到的注解
    @Configuration 指定類是一個(gè)配置類
    @ConditionalOnXXX 在指定條件成立的情況下自動(dòng)配置生效魄咕、
    @AutoConfigureAfter 在特定自動(dòng)裝配class之后(指定自動(dòng)配置類的順序)
    @AutoConfigureBefore 在特定自動(dòng)裝配class之前(指定自動(dòng)配置類的順序)
    @AutoConfigureOrder 指定順序
    @Bean 給容器中添加組件
    @ConfigurationPropertie 結(jié)合相關(guān)xxxProperties類來綁定相關(guān)的配置。
    @EnableConfigurationProperties 讓xxxProperties生效加入到容器中阵谚。

  2. 加載方式
    自動(dòng)配置類要能加載,將需要啟動(dòng)就加載的自動(dòng)配置類烟具,配置在META-INF/spring.factories文件中梢什。

  3. 啟動(dòng)器模式
    啟動(dòng)器只用來做依賴導(dǎo)入,專門寫一個(gè)自動(dòng)配置模塊朝聋。啟動(dòng)器依賴自動(dòng)配置嗡午,使用時(shí)只需要引入啟動(dòng)器(starter)。

  4. 啟動(dòng)器規(guī)則
    啟動(dòng)器就是個(gè)空jar文件冀痕,僅提供輔助性依賴管理荔睹,這些依賴可能用于自動(dòng)裝配或者其他庫。

命名規(guī)范:
- 推薦使用以下命名規(guī)范
- 官方命名空間
?- 前綴:spring-boot-starter-
?- 模式:spring-boot-starter-模塊名
- 自定義命名空間
? - 后綴:-spring-boot-starter
? - 模式:模塊名-spring-boot-starter

  1. 編寫一個(gè)啟動(dòng)器模塊
    1). 啟動(dòng)器模塊
<?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.desperado.starter</groupId>
    <artifactId>desperado-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!-- 啟動(dòng)器 -->
    <dependencies>
        <!-- 引入自動(dòng)配置模塊 -->
        <dependency>
            <groupId>com.desperado.starter</groupId>
            <artifactId>desperado-spring-boot-starter-autoconfigurer</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>

2). 自動(dòng)配置模塊

<?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.desperado.starter</groupId>
    <artifactId>desperado-spring-boot-starter-autoconfigurer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <!--  引入spring-boot-starter言蛇;所有starter的基本配置 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

    </dependencies>

</project>

3). 編寫配置文件類

@ConfigurationProperties(prefix = "desperado.custom")
public class CustomProperties {
    
    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;
    }
}

4). 進(jìn)行配置文件的處理

public class CustomService {
    CustomProperties customProperties;

    public CustomProperties getCustomProperties(){
        return customProperties;
    }

    public void setCustomProperties(CustomProperties customProperties){
        this.customProperties = customProperties;
    }

    public String sayHello(String name){
        return customProperties.getPrefix()+"-"+name+customProperties.getSuffix();
    }
}

5). 編寫自動(dòng)配置類

@ConditionalOnWebApplication
@EnableConfigurationProperties(CustomProperties.class)
public class CustomServiceAutoConfiguration {
    
    @Autowired
    CustomProperties customProperties;
    
    @Bean
    public CustomService customService(){
        CustomService customService = new CustomService();
        customService.setCustomProperties(customProperties);
        return customService;
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末僻他,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子腊尚,更是在濱河造成了極大的恐慌吨拗,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異劝篷,居然都是意外死亡哨鸭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門娇妓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來像鸡,“玉大人,你說我怎么就攤上這事哈恰≈还溃” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵蕊蝗,是天一觀的道長仅乓。 經(jīng)常有香客問我,道長蓬戚,這世上最難降的妖魔是什么夸楣? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮子漩,結(jié)果婚禮上豫喧,老公的妹妹穿的比我還像新娘。我一直安慰自己幢泼,他們只是感情好紧显,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缕棵,像睡著了一般孵班。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上招驴,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天篙程,我揣著相機(jī)與錄音,去河邊找鬼别厘。 笑死虱饿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的触趴。 我是一名探鬼主播氮发,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼冗懦!你這毒婦竟也來了爽冕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤披蕉,失蹤者是張志新(化名)和其女友劉穎扇售,沒想到半個(gè)月后前塔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡承冰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年华弓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片困乒。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寂屏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娜搂,到底是詐尸還是另有隱情迁霎,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布百宇,位于F島的核電站考廉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏携御。R本人自食惡果不足惜昌粤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啄刹。 院中可真熱鬧涮坐,春花似錦、人聲如沸誓军。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昵时。三九已至捷雕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壹甥,已是汗流浹背救巷。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盹廷,地道東北人征绸。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓久橙,卻偏偏與公主長得像俄占,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淆衷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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