Springboot--擴(kuò)展外部化配置(三)

接著之前Springboot--擴(kuò)展外部化配置(二) 來繼續(xù)實現(xiàn)外部化的擴(kuò)展别惦。我們將剩余的幾個擴(kuò)展給介紹完畢。

  • 基于 ApplicationContextInitializer 擴(kuò)展外部化配置屬性源
  • 基于 SpringApplicationRunListener#contextPrepared 擴(kuò)展外部化配置屬性源
  • 基于 SpringApplicationRunListener#contextLoaded 擴(kuò)展外部化配置屬性源

前置內(nèi)容

在Springboot--擴(kuò)展外部化配置(一) 中我們有了解到在SpringBoot啟動過程中在創(chuàng)建完 Environment之后,就會準(zhǔn)備上下文org.springframework.boot.SpringApplication#prepareContext 。我們接下來的三種外部化配置實現(xiàn)方式 都是基于這個方法的執(zhí)行順序?qū)崿F(xiàn)的÷ゾ欤可以詳細(xì)看一下貼出的源碼中的注釋。

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
    //執(zhí)行ApplicationContextInitializer SPI接口 initialize
    //用于 基于 ApplicationContextInitializer 擴(kuò)展外部化配置屬性源
   applyInitializers(context);
    
   //SpringApplicationRunListener 實現(xiàn)類遍歷監(jiān)聽contextPrepared
   //用于 基于 SpringApplicationRunListener#contextPrepared 擴(kuò)展外部化配置屬性源
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }

   // Add boot specific singleton beans
   context.getBeanFactory().registerSingleton("springApplicationArguments",
         applicationArguments);
   if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
   }

   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
   //SpringApplicationRunListener 實現(xiàn)類遍歷監(jiān)聽contextLoaded
    //用于 基于 SpringApplicationRunListener#contextLoaded 擴(kuò)展外部化配置屬性源
   listeners.contextLoaded(context);
}

通過我標(biāo)注注釋的地方熊尉,大家可能也就了解到了之后我們準(zhǔn)備怎么去實現(xiàn)剩下的外部化配置方式罐柳。其實無非就是通過SpringBoot在初始化上下文之前,調(diào)用可以調(diào)用的對外接口狰住,進(jìn)行配置张吉。

這里我們主要是注意因為我們在擴(kuò)展外部化配置時 使用的是 org.springframework.core.env.MutablePropertySources#addFirst 方法,默認(rèn)會把配置的外部化資源放在第一位催植。所以我們要注意prepareContext() 方法中的這些過程的執(zhí)行順序肮蛹,換局話說,執(zhí)行越晚创南,那么其配置的資源則會放在最前面伦忠。

基于 ApplicationContextInitializer 擴(kuò)展外部化配置屬性源

實現(xiàn)基礎(chǔ)

現(xiàn)在我們就使用在 org.springframework.boot.SpringApplication#prepareContext中的 applyInitializers(context); 方法(目的是對應(yīng)用程序上下文進(jìn)行初始化),做切入點(diǎn)扰藕,讓我們也簡單實現(xiàn)一個自定義外部資源的初始化操作缓苛。

具體實現(xiàn)

1.實現(xiàn)ApplicationContextInitializer `接口類 ExternalizeApplicationContextInitializer.java

/**
 * @ClassName ExternalizeApplicationContextInitializer
 * @Description 自定義上下文初始化實現(xiàn)類
 * @Author Neal
 * @Date 2019/1/23 19:05
 * @Version 1.0
 */
public class ExternalizeApplicationContextInitializer implements ApplicationContextInitializer {


    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //獲取environment 對象
        ConfigurableEnvironment environment = applicationContext.getEnvironment();

        //獲取項目跟路徑
        String classpath = ExternalizeApplicationContextInitializer.class.getResource("/").getPath();
        //獲取PropertySource組合對象
        MutablePropertySources propertySources = environment.getPropertySources();
        //獲取自定義的外部化配置資源
        File file = new File(classpath +"config/applicationinitializer.properties");

        /**
         * 獲取Property對象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //聲明一個properties對象
            Properties properties = new Properties();
            // 加載字符流成為 Properties 對象
            properties.load(reader);
            //聲明Spring內(nèi)置PropertiesPropertySource對象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---ExternalizeApplicationContextInitializer",properties);
            //將配置資源放到其他配置資源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

2.配置自定義的外部化資源,resources\config\applicationinitializer.properties

#ExternalizeApplicationContextInitializer 對應(yīng)的配置
test=5

3.將自定義的ApplicationContextInitializer 加入到 resources\META-INF\spring.factories

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
neal.externalized.listener.ExternalizePropertyListener
#ApplicationListener
org.springframework.context.ApplicationListener=\
neal.externalized.listener.ExternalizePropertyEventListener

#EnvironmentPostProcessor
org.springframework.boot.env.EnvironmentPostProcessor=\
neal.externalized.listener.ExternalizePropertyPostProcessor

#ApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer=\
neal.externalized.listener.ExternalizeApplicationContextInitializer

執(zhí)行結(jié)果

我們啟動容器邓深,查看控制臺輸出

p8.png

這里我們注意到未桥,我們的測試的值已經(jīng)變成了所配置的5,但是還有一點(diǎn) ConfigurationPropertySourcesPropertySource 這個類的執(zhí)行是在 我們自定義ExternalizeApplicationContextInitializer之前的芥备,所以我們剛剛自定義外部化配置才會放在首位執(zhí)行冬耿。

基于 SpringApplicationRunListener#contextPrepared 擴(kuò)展外部化配置屬性源

實現(xiàn)基礎(chǔ)

使用在 org.springframework.boot.SpringApplication#prepareContext中的 listeners.contextPrepared(context); 方法(上下文準(zhǔn)備完成),做切入點(diǎn)萌壳,簡單實現(xiàn)一個自定義外部資源的初始化操作亦镶。

這段的實現(xiàn)其實在我們之前已經(jīng)自定義了SpringApplicationRunListener 事件監(jiān)聽,就是我們介紹的第一種擴(kuò)展方式(基于 SpringApplicationRunListener#environmentPrepared的實現(xiàn)方式)中使用的ExternalizePropertyListener袱瓮。

具體實現(xiàn)

1.重寫ExternalizePropertyListener#contextPrepared() 方法缤骨。這里用到了讀取Properties文件的方法,由于是DEMO尺借,我就不單獨(dú)抽出來重構(gòu)了绊起。

/**
 * 擴(kuò)展 {@link PropertySource}
 */
public class ExternalizePropertyListener implements SpringApplicationRunListener,Ordered {

    private final SpringApplication application;

    private final String[] args;

    public ExternalizePropertyListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

    @Override
    public void starting() {

    }

    /**
     * 基于 `SpringApplicationRunListener#environmentPrepared`的實現(xiàn)方式
     * @param environment
     */
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

        //獲取項目跟路徑
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //獲取PropertySource組合對象
        MutablePropertySources propertySources = environment.getPropertySources();

        //獲取自定義的外部化配置資源
        File file = new File(classpath +"config/externalizepropertylistener.properties");
        /**
         * 獲取Property對象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //聲明一個properties對象
            Properties properties = new Properties();
            // 加載字符流成為 Properties 對象
            properties.load(reader);
            //聲明Spring內(nèi)置PropertiesPropertySource對象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---ExternalizePropertyListener",properties);
            //將配置資源放到其他配置資源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     *     基于 `SpringApplicationRunListener#contextPrepared` 擴(kuò)展外部化配置屬性源
     */
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        //獲取environment 對象
        ConfigurableEnvironment environment = context.getEnvironment();

        //獲取項目跟路徑
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //獲取PropertySource組合對象
        MutablePropertySources propertySources = environment.getPropertySources();
        //獲取自定義的外部化配置資源
        File file = new File(classpath +"config/contextprepared.properties");

        /**
         * 獲取Property對象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //聲明一個properties對象
            Properties properties = new Properties();
            // 加載字符流成為 Properties 對象
            properties.load(reader);
            //聲明Spring內(nèi)置PropertiesPropertySource對象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---contextPrepared",properties);
            //將配置資源放到其他配置資源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

    }

    @Override
    public void started(ConfigurableApplicationContext context) {

    }

    @Override
    public void running(ConfigurableApplicationContext context) {

    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {

    }
    //加載順序在EventPublishingRunListener之前
    // 這么做是為了之后的外部化配置展示
    @Override
    public int getOrder() {
        return -1;
    }
}

這里我貼出了全部方法,在這一部分燎斩,我們只需要關(guān)心#contextPrepared()的實現(xiàn)虱歪。

2.配置自定義的外部化資源,resources\config\contextprepared.properties

#SpringApplicationRunListener#contextPrepared 對應(yīng)的配置
test=6

3.由于之前在resources\META-INF\spring.factories添加過監(jiān)聽栅表,所以這里不需要做任何操作

執(zhí)行結(jié)果

p9.png

執(zhí)行結(jié)果笋鄙,不出意外,確實是我們最新配置的值怪瓶。

基于 SpringApplicationRunListener#contextLoaded 擴(kuò)展外部化配置屬性源

實現(xiàn)基礎(chǔ)

我們接著使用在 org.springframework.boot.SpringApplication#prepareContext中的 listeners.contextLoaded(context); 方法(加載應(yīng)用上下文)萧落,做切入點(diǎn),簡單實現(xiàn)一個自定義外部資源的初始化操作洗贰。

同樣找岖,這段的實現(xiàn)跟上面的實現(xiàn)一樣,都是在之前介紹的第一種擴(kuò)展方式(基于 SpringApplicationRunListener#environmentPrepared的實現(xiàn)方式)中使用的ExternalizePropertyListener哆姻。

具體實現(xiàn)

1.重寫ExternalizePropertyListener#contextLoaded()方法宣增。這里一樣我們直接copy之前的實現(xiàn),不做重構(gòu)矛缨。

/**
 * 擴(kuò)展 {@link PropertySource}
 */
public class ExternalizePropertyListener implements SpringApplicationRunListener,Ordered {

    private final SpringApplication application;

    private final String[] args;

    public ExternalizePropertyListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

    @Override
    public void starting() {

    }

    /**
     * 基于 `SpringApplicationRunListener#environmentPrepared`的實現(xiàn)方式
     * @param environment
     */
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

        //獲取項目跟路徑
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //獲取PropertySource組合對象
        MutablePropertySources propertySources = environment.getPropertySources();

        //獲取自定義的外部化配置資源
        File file = new File(classpath +"config/externalizepropertylistener.properties");
        /**
         * 獲取Property對象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //聲明一個properties對象
            Properties properties = new Properties();
            // 加載字符流成為 Properties 對象
            properties.load(reader);
            //聲明Spring內(nèi)置PropertiesPropertySource對象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---ExternalizePropertyListener",properties);
            //將配置資源放到其他配置資源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     *     基于 `SpringApplicationRunListener#contextPrepared` 擴(kuò)展外部化配置屬性源
     */
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        //獲取environment 對象
        ConfigurableEnvironment environment = context.getEnvironment();

        //獲取項目跟路徑
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //獲取PropertySource組合對象
        MutablePropertySources propertySources = environment.getPropertySources();
        //獲取自定義的外部化配置資源
        File file = new File(classpath +"config/contextprepared.properties");

        /**
         * 獲取Property對象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //聲明一個properties對象
            Properties properties = new Properties();
            // 加載字符流成為 Properties 對象
            properties.load(reader);
            //聲明Spring內(nèi)置PropertiesPropertySource對象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---contextPrepared",properties);
            //將配置資源放到其他配置資源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 基于 `SpringApplicationRunListener#contextLoaded` 擴(kuò)展外部化配置屬性源
     * @param context
     */
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        //獲取environment 對象
        ConfigurableEnvironment environment = context.getEnvironment();

        //獲取項目跟路徑
        String classpath = ExternalizePropertyListener.class.getResource("/").getPath();
        //獲取PropertySource組合對象
        MutablePropertySources propertySources = environment.getPropertySources();
        //獲取自定義的外部化配置資源
        File file = new File(classpath +"config/contextploaded.properties");

        /**
         * 獲取Property對象
         */
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream(file));
            //聲明一個properties對象
            Properties properties = new Properties();
            // 加載字符流成為 Properties 對象
            properties.load(reader);
            //聲明Spring內(nèi)置PropertiesPropertySource對象
            PropertiesPropertySource propertySource = new PropertiesPropertySource("from---contextLoaded",properties);
            //將配置資源放到其他配置資源的首位
            propertySources.addFirst(propertySource);
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void started(ConfigurableApplicationContext context) {

    }

    @Override
    public void running(ConfigurableApplicationContext context) {

    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {

    }
    //加載順序在EventPublishingRunListener之前
    // 這么做是為了之后的外部化配置展示
    @Override
    public int getOrder() {
        return -1;
    }
}

這里也貼出了全部方法爹脾,在這一部分,我們只需要關(guān)心#contextLoaded()的實現(xiàn)箕昭。

2.配置自定義的外部化資源灵妨,resources\config\contextloaded.properties

#ExternalizePropertyListener.contextLoaded 對應(yīng)的配置
test=7

3.同樣,之前在resources\META-INF\spring.factories添加過監(jiān)聽,所以這里不需要做任何操作落竹。

執(zhí)行結(jié)果

p10.png

不出意外泌霍,我們也同樣改變了擴(kuò)展的值。

小結(jié)

對于外部化配置的方面述召,相信在之后的開發(fā)中會有很大的用途朱转,特別是各種整合蟹地。希望在接下來的實際開發(fā)中,我們可以用到這些實現(xiàn)藤为。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怪与,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子缅疟,更是在濱河造成了極大的恐慌分别,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件存淫,死亡現(xiàn)場離奇詭異耘斩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)桅咆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門括授,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轧邪,你說我怎么就攤上這事刽脖。” “怎么了忌愚?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵曲管,是天一觀的道長。 經(jīng)常有香客問我硕糊,道長院水,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任简十,我火速辦了婚禮檬某,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘螟蝙。我一直安慰自己恢恼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布胰默。 她就那樣靜靜地躺著场斑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牵署。 梳的紋絲不亂的頭發(fā)上漏隐,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音奴迅,去河邊找鬼青责。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脖隶。 我是一名探鬼主播扁耐,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浩村!你這毒婦竟也來了做葵?” 一聲冷哼從身側(cè)響起占哟,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤心墅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后榨乎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怎燥,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年蜜暑,在試婚紗的時候發(fā)現(xiàn)自己被綠了铐姚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡肛捍,死狀恐怖隐绵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拙毫,我是刑警寧澤依许,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站缀蹄,受9級特大地震影響峭跳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缺前,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一蛀醉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衅码,春花似錦拯刁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惹恃,卻和暖如春夭谤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巫糙。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工朗儒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓醉锄,卻偏偏與公主長得像乏悄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恳不,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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