日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

SpringBoot启动如何加载application.yml配置文件

發(fā)布時(shí)間:2024/9/30 javascript 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot启动如何加载application.yml配置文件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、前言

在spring時(shí)代配置文件的加載都是通過web.xml配置加載的(Servlet3.0之前),可能配置方式有所不同,但是大多數(shù)都是通過指定路徑的文件名的形式去告訴spring該加載哪個(gè)文件;

<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/application*.xml</param-value> </context-param>

而到了springboot時(shí)代,我們發(fā)現(xiàn)原來熟悉的web.xml已不復(fù)存在,但是springboot卻依然可以找到默認(rèn)的配置文件(application.yml),那它是如何實(shí)現(xiàn)的呢?今天我們就一起來探究一下springboot自動(dòng)加載配置文件的機(jī)制!

看完本篇文章你將了解到:

  • springboot什么時(shí)候加載配置文件
  • springboot通過哪個(gè)類加載配置文件
  • springboot自動(dòng)加載配置文件流程
  • 激活文件優(yōu)先級(jí)
  • 文件加載路徑優(yōu)先級(jí)
  • 文件后綴優(yōu)先級(jí)
  • 二、提出猜想

    我們知道在使用springboot中我們只要在resources下面新建一個(gè)application.yml文件他就會(huì)自動(dòng)加載,那是不是springboot默認(rèn)在哪里配置了這個(gè)路徑和文件名?

    三、驗(yàn)證猜想

    為了證實(shí)我們的猜想,我們可以通過查看springboot項(xiàng)目源碼,跟著debug一步一步走;
    這里我使用的是springboot2.0版本,2.0與1.5版本比較啟動(dòng)的大體流程是一樣的,只不過在一些實(shí)現(xiàn)中有所差異;

    1.啟動(dòng)流程

    要知道springboot如何加載配置文件,就需要了解它的啟動(dòng)流程:

    我們從main方法進(jìn)入,大概的調(diào)用流程如下:

    DemoApplication.main->SpringApplication.run->new SpringApplication().run


    其實(shí)啟動(dòng)的主要過程都在new SpringApplication().run();

    • new SpringApplication():創(chuàng)建SpringApplication實(shí)例,負(fù)責(zé)加載配置一些基本的環(huán)境變量、資源、構(gòu)造器、監(jiān)聽器
    • run():負(fù)責(zé)springboot整個(gè)啟動(dòng)過程,包括加載創(chuàng)建環(huán)境、打印banner、配置文件、配置應(yīng)用上下文,加載bean等等sb整個(gè)生命周期幾乎都在run方法中;

    今天我們的主題是sb如何加載配置文件,所以著重講解加載配置文件和之前的操作原理和源碼,其他的功能以后有機(jī)會(huì)再和大家一起研究,下面我們來看看new SpringApplication()做了什么操作;

    2.創(chuàng)建SpringApplication實(shí)例

    /*** 創(chuàng)建一個(gè)SpringApplication實(shí)體,應(yīng)用程序上下文將從指定的主源文檔加載bean以獲取詳細(xì)信息,* 這個(gè)實(shí)例可以在調(diào)用之前自定義* @param resourceLoader* @param primarySources*/ @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {//使用的資源加載器this.resourceLoader = resourceLoader;//主要的bean資源 primarySources【在這里是啟動(dòng)類所在的.class】,不能為null,如果為null,拋異常Assert.notNull(primarySources, "PrimarySources must not be null");//啟動(dòng)類的實(shí)例數(shù)組轉(zhuǎn)化成list,放在LinkedHashSet集合中this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));/*** 創(chuàng)建應(yīng)用類型,不同應(yīng)用程序類型,創(chuàng)建不同的環(huán)境* springboot1.5 只有兩種類型:web環(huán)境和非web環(huán)境* springboot2.0 有三種應(yīng)用類型:WebApplicationType* NONE:不需要再web容器的環(huán)境下運(yùn)行,也就是普通的工程* SERVLET:基于servlet的Web項(xiàng)目* REACTIVE:響應(yīng)式web應(yīng)用reactive web Spring5版本的新特性*/this.webApplicationType = WebApplicationType.deduceFromClasspath();/*** 每一個(gè)initailizer都是一個(gè)實(shí)現(xiàn)了ApplicationContextInitializer接口的實(shí)例。* ApplicationContextInitializer是Spring IOC容器中提供的一個(gè)接口: void initialize(C applicationContext);* 這個(gè)方法它會(huì)在ConfigurableApplicationContext的refresh()方法調(diào)用之前被調(diào)用(prepareContext方法中調(diào)用),* 做一些容器的初始化工作。*/setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));/*** Springboot整個(gè)生命周期在完成一個(gè)階段的時(shí)候都會(huì)通過事件推送器(EventPublishingRunListener)產(chǎn)生一個(gè)事件(ApplicationEvent),* 然后再遍歷每個(gè)監(jiān)聽器(ApplicationListener)以匹配事件對(duì)象,這是一種典型的觀察者設(shè)計(jì)模式的實(shí)現(xiàn)* 具體事件推送原理請(qǐng)看:sb事件推送機(jī)制圖*/setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 指定main函數(shù)啟動(dòng)所在的類,即啟動(dòng)類BootApplication.classthis.mainApplicationClass = deduceMainApplicationClass(); }

    我們來大概的看下ApplicationListener的一些實(shí)現(xiàn)類以及他們具體的功能簡(jiǎn)介

    這些監(jiān)聽器的實(shí)現(xiàn)類都是在spring.factories文件中配置好的,代碼中通過getSpringFactoriesInstances方法獲取,這種機(jī)制叫做SPI機(jī)制:通過本地的注冊(cè)發(fā)現(xiàn)獲取到具體的實(shí)現(xiàn)類,輕松可插拔。

    SpringBoot默認(rèn)情況下提供了兩個(gè)spring.factories文件,分別是:

    spring-boot-2.0.2.RELEASE.jar
    spring-boot-autoconfigure-2.0.2.RELEASE.jar

    概括來說在創(chuàng)建SpringApplication實(shí)例的時(shí)候,sb會(huì)加載一些初始化和啟動(dòng)的參數(shù)與類,如同跑步比賽時(shí)的等待發(fā)令槍的階段;

    3.run方法

    (1)、事件推送原理

    SB啟動(dòng)過程中分多個(gè)階段或者說是多個(gè)步驟,每完成一步就會(huì)產(chǎn)生一個(gè)事件,并調(diào)用對(duì)應(yīng)事件的監(jiān)聽器,這是一種標(biāo)準(zhǔn)的觀察者模式,這在啟動(dòng)的過程中有很好的擴(kuò)展性,下面我們來看看sb的事件推送原理:
    SpringBoot事件推送原理圖:

    (2)、run方法整體流程簡(jiǎn)述

    /*** 運(yùn)行應(yīng)用程序,創(chuàng)建并刷新一個(gè)新的應(yīng)用程序上下文** @param args* @return*/ public ConfigurableApplicationContext run(String... args) {/*** StopWatch: 簡(jiǎn)單的秒表,允許定時(shí)的一些任務(wù),公開每個(gè)指定任務(wù)的總運(yùn)行時(shí)間和運(yùn)行時(shí)間。* 這個(gè)對(duì)象的設(shè)計(jì)不是線程安全的,沒有使用同步。SpringApplication是在單線程環(huán)境下,使用安全。*/StopWatch stopWatch = new StopWatch();// 設(shè)置當(dāng)前啟動(dòng)的時(shí)間為系統(tǒng)時(shí)間startTimeMillis = System.currentTimeMillis();stopWatch.start();// 創(chuàng)建一個(gè)應(yīng)用上下文引用ConfigurableApplicationContext context = null;// 異常收集,報(bào)告啟動(dòng)異常Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();/*** 系統(tǒng)設(shè)置headless模式(一種缺乏顯示設(shè)備、鍵盤或鼠標(biāo)的環(huán)境下,比如服務(wù)器),* 通過屬性:java.awt.headless=true控制*/configureHeadlessProperty();/** 獲取事件推送監(jiān)器,負(fù)責(zé)產(chǎn)生事件,并調(diào)用支某類持事件的監(jiān)聽器* 事件推送原理看上面的事件推送原理圖*/SpringApplicationRunListeners listeners = getRunListeners(args);/*** 發(fā)布一個(gè)啟動(dòng)事件(ApplicationStartingEvent),通過上述方法調(diào)用支持此事件的監(jiān)聽器*/listeners.starting();try {// 提供對(duì)用于運(yùn)行SpringApplication的參數(shù)的訪問。取默認(rèn)實(shí)現(xiàn)ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);/*** 構(gòu)建容器環(huán)境,這里加載配置文件*/ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 對(duì)環(huán)境中一些bean忽略配置configureIgnoreBeanInfo(environment);// 日志控制臺(tái)打印設(shè)置Banner printedBanner = printBanner(environment);// 創(chuàng)建容器context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);/*** 準(zhǔn)備應(yīng)用程序上下文* 追蹤源碼prepareContext()進(jìn)去我們可以發(fā)現(xiàn)容器準(zhǔn)備階段做了下面的事情:* 容器設(shè)置配置環(huán)境,并且監(jiān)聽容器,初始化容器,記錄啟動(dòng)日志,* 將給定的singleton對(duì)象添加到此工廠的singleton緩存中。* 將bean加載到應(yīng)用程序上下文中。*/prepareContext(context, environment, listeners, applicationArguments, printedBanner);/*** 刷新上下文* 1、同步刷新,對(duì)上下文的bean工廠包括子類的刷新準(zhǔn)備使用,初始化此上下文的消息源,注冊(cè)攔截bean的處理器,檢查偵聽器bean并注冊(cè)它們,實(shí)例化所有剩余的(非延遲-init)單例。* 2、異步開啟一個(gè)同步線程去時(shí)時(shí)監(jiān)控容器是否被關(guān)閉,當(dāng)關(guān)閉此應(yīng)用程序上下文,銷毀其bean工廠中的所有bean。* 。。。底層調(diào)refresh方法代碼量較多*/refreshContext(context);afterRefresh(context, applicationArguments);// stopwatch 的作用就是記錄啟動(dòng)消耗的時(shí)間,和開始啟動(dòng)的時(shí)間等信息記錄下來stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 發(fā)布一個(gè)已啟動(dòng)的事件listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 發(fā)布一個(gè)運(yùn)行中的事件listeners.running(context);}catch (Throwable ex) {// 啟動(dòng)異常,里面會(huì)發(fā)布一個(gè)失敗的事件handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context; }

    (3)、構(gòu)建容器環(huán)境

    在:run方法中的ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);是準(zhǔn)備環(huán)境,里面會(huì)加載配置文件;

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {// 創(chuàng)建一個(gè)配置環(huán)境,根據(jù)前面定義的應(yīng)用類型定義不同的環(huán)境ConfigurableEnvironment environment = getOrCreateEnvironment();// 將配置參數(shù)設(shè)置到配置環(huán)境中configureEnvironment(environment, applicationArguments.getSourceArgs());/*** 發(fā)布一個(gè)環(huán)境裝載成功的事件,并調(diào)用支持此事件的監(jiān)聽器* 這其中就有我們今天的主角:配置文件加載監(jiān)聽器(ConfigFileApplicationListener)*/listeners.environmentPrepared(environment);// 將配置環(huán)境綁定到應(yīng)用程序bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment; }

    (4)、ConfigFileApplicationListener類介紹

    sb就是通過ConfigFileApplicationListener 這個(gè)類來加載配置文件的,這個(gè)類同樣是一個(gè)監(jiān)聽器,我們來看看他的繼承類圖:

    再讓我們來看看這個(gè)類具體都有哪些方法:

    最后我們來看看這個(gè)類有哪些需要注意的字段:

    (5)、ConfigFileApplicationListener類加載配置文件

    我們從ConfigFileApplicationListener.onApplicationEvent開始,一直往下看方法鏈,發(fā)現(xiàn)最后是load方法去具體怎么加載配置文件的

    激活配置文件與默認(rèn)配置文件的優(yōu)先級(jí):
    我們?cè)谑褂弥薪?jīng)常會(huì)根據(jù)不同的環(huán)境根據(jù)spring.profiles.active屬性來定義不同的配置文件:

    • application-dev.properties
    • application-test.properties
    • application-prod.properties

    但同時(shí)我們會(huì)創(chuàng)建一個(gè)默認(rèn)的配置文件:application.properties,那自定義環(huán)境的配置文件與默認(rèn)的配置文件的優(yōu)先級(jí)是哪個(gè)高呢?

    看圖片我們可知他們加載的先后順序(注意:后加載會(huì)覆蓋前加載的文件):

    • application-xxx.properties
    • application.properties

    配置文件路徑的優(yōu)先級(jí):
    我們從屬性:DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/可以看出文件路徑的先后順序(注意:后加載的會(huì)覆蓋先加載的):

    • classpath:/
    • classpath:/config/
    • file:./
    • file:./config/

    配置文件的優(yōu)先級(jí):
    我們從這個(gè)類中的字段:propertySourceLoaders可以看出有兩個(gè)Loader,請(qǐng)各位看官看圖:


    我們從上面兩張圖中可以看出,每個(gè)Loader會(huì)加載兩種后綴名的文件,加起來就是4種,又因?yàn)槭菙?shù)組類型,所以也會(huì)有先后順序,所以加載配置文件的先后順序就是(后加載覆蓋先加載的):

    • properties
    • xml
    • yml
    • yaml

    最后查找的具體路徑:location + name + "-" + profile + "." + ext

    這里我們介紹了三種優(yōu)先級(jí):

  • active與默認(rèn)優(yōu)先級(jí)
  • 文件路徑優(yōu)先級(jí)
  • 文件后綴優(yōu)先級(jí)
    未完待續(xù)。。。
  • 四、提問

    springboot學(xué)習(xí)遺留問題,
    1.active和默認(rèn)的誰(shuí)覆蓋誰(shuí)
    2.flter區(qū)別
    3.多個(gè)配置文件如何覆蓋

    更多Java優(yōu)質(zhì)文章,請(qǐng)關(guān)注豬哥微信公眾號(hào):豬哥Java!

    總結(jié)

    以上是生活随笔為你收集整理的SpringBoot启动如何加载application.yml配置文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。