Apollo 5 — 教你怎么把自己的配置放到 Spring 環(huán)境中

目錄:

  1. 前言
  2. 處理方案
  3. 簡單例子

前言

有的時候,你可能需要在 Spring 環(huán)境中放入一些配置库快,但這些配置無法寫死在配置文件中摸袁,只能運(yùn)行時放入。那么义屏,這個時候該怎么辦呢靠汁?

Apollo 就是搞配置的,那么自然會遇到這個問題闽铐,他是如何處理的呢蝶怔?

處理方案

首先要知道 Spring 環(huán)境中,一個配置的數(shù)據(jù)結(jié)構(gòu)是什么兄墅?

是抽象類 PropertySource<T>踢星, 內(nèi)部是個 key value 結(jié)構(gòu)。這個 T 可以是任意類型隙咸,取決于子類的設(shè)計沐悦。

子類可以通過重寫 getProperty 抽象方法獲取配置。

Spring 自身的 org.springframework.core.env.MapPropertySource 就重寫了這個方法五督。

public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {

    public MapPropertySource(String name, Map<String, Object> source) {
        super(name, source);
    }


    @Override
    public Object getProperty(String name) {
        return this.source.get(name);
    }

    @Override
    public boolean containsProperty(String name) {
        return this.source.containsKey(name);
    }

    @Override
    public String[] getPropertyNames() {
        return StringUtils.toStringArray(this.source.keySet());
    }
}

可以看到藏否,他的泛型是 Map,getProperty 方法則是從 Map 中獲取充包。

Apollo 就直接利用了這個類副签。

兩個不同的子類,不同的刷新邏輯。我們暫時不關(guān)心他們的不同继薛。

這兩個類都會被 RefreshableConfig 組合修壕,添加到 Spring 的環(huán)境中。

import org.springframework.core.env.ConfigurableEnvironment;

public abstract class RefreshableConfig {

  @Autowired
  private ConfigurableEnvironment environment; // Spring 環(huán)境

  @PostConstruct
  public void setup() {
  // 省略代碼
    for (RefreshablePropertySource propertySource : propertySources) {
      propertySource.refresh();
      // 注意:成功刷新后遏考,放到 Spring 的環(huán)境中
      environment.getPropertySources().addLast(propertySource);
    }
  // 省略代碼

當(dāng)從 Spring 的環(huán)境中獲取配置的時候慈鸠,具體代碼是下面這樣的:

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
        for (PropertySource<?> propertySource : this.propertySources) {
            // 注意:這里調(diào)用的就是 propertySource.getProperty 方法,子類剛剛重寫的方法
            Object value = propertySource.getProperty(key);
             // 省略無關(guān)代碼........
            return convertValueIfNecessary(value, targetValueType);
        }
        return null;
}

Spring 維護(hù)了一個 PropertySource 的集合灌具,這個結(jié)合是有順序的青团,也就是說,排在最前面的優(yōu)先級最高(遍歷從下標(biāo) 0 開始)咖楣。

而用戶可以在 PropertySource 里督笆,維護(hù)一個配置字典(Map),這樣诱贿,就類似 2 維數(shù)組的這樣一個數(shù)據(jù)結(jié)構(gòu)娃肿。

所以,配置是可以重名的珠十,重名時料扰,以最前面的 PropertySource 中的配置為準(zhǔn)。所以焙蹭,Spring 留給了幾個 API:

  1. addFirst(PropertySource<?> propertySource)
  2. addLast(PropertySource<?> propertySource)
  3. addBefore(String relativePropertySourceName, PropertySource<?> propertySource)
  4. addAfter(String relativePropertySourceName, PropertySource<?> propertySource)

從名字可以看出晒杈,通過這些 API,我們可以將 propertySource 插入到我們指定的地方孔厉。從而可以手動控制配置的優(yōu)先級拯钻。

Spring 中有個現(xiàn)成的 CompositePropertySource 類,內(nèi)部聚合了一個 PropertySource Set 集合撰豺,當(dāng) getProperty(String name) 的時候粪般,就會遍歷這個集合,然后調(diào)用這個 propertySource 的 getProperty(name) 方法郑趁。相當(dāng)于 3 維數(shù)組刊驴。

大概的設(shè)計是這樣:

一個環(huán)境中,有多個 PS(PropertySource 簡稱)寡润,每個 PS 可以直接包含配置,也可以再包裝一層 PS舅柜。

簡單例子

我們這里有個簡單的例子梭纹,需求:
程序里有個配置,但不能寫死在配置文件中致份,只能在程序啟動過程中進(jìn)行配置变抽,然后注入到 Spring 環(huán)境中,讓 Spring 在之后的 IOC 中,可以正常的使用這些配置绍载。

代碼如下:

@SpringBootApplication
public class DemoApplication {

  @Value("${timeout:1}")
  String timeout;


  public static void main(String[] args) throws InterruptedException {
    ApplicationContext c = SpringApplication.run(DemoApplication.class, args);
    for (; ; ) {
      Thread.sleep(1000);
      System.out.println(c.getBean(DemoApplication.class).timeout);
    }
  }
}

application.properties 配置文件

timeout=100

上面的代碼中诡宗,我們在 bean 中定義了一個屬性 timeout, 并在本地配置文件中寫入了一個 100 的值击儡,也在表達(dá)式中給了一個默認(rèn)值 1塔沃。

那么現(xiàn)在打印出來的就是配置文件中的值:100.

但是,這不是我們想要的結(jié)果阳谍,所以需要修改代碼蛀柴。

我們加入一個類:

@Component
class Test implements EnvironmentAware, BeanFactoryPostProcessor {

  @Override
  public void setEnvironment(Environment environment) {
    ((ConfigurableEnvironment) environment).getPropertySources()
        // 這里是 addFirst,優(yōu)先級高于 application.properties 配置
        .addFirst(new PropertySource<String>("timeoutConfig", "12345") {
          // 重點
          @Override
          public Object getProperty(String s) {
            if (s.equals("timeout")) {//
              return source;// 返回構(gòu)造方法中的 source :12345
            }
            return null;
          }
        });
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
      throws BeansException {
    // NOP
  }
}

運(yùn)行之后,結(jié)果:12345

2018-07-02 15:26:54.315  INFO 43393 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-07-02 15:26:54.327  INFO 43393 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.991 seconds (JVM running for 1.49)
12345
12345

為什么加入了這個類矫夯,就能夠代替配置文件中的屬性呢鸽疾?解釋一下這個類的作用。

我們要做的事情就是在 Spring 的環(huán)境中训貌,插入自定義的 PS 對象制肮,以便容器獲取的時候,能夠通過 getProperty 方法獲取對應(yīng)的配置递沪。

所以豺鼻,我們要拿到 Spring 環(huán)境對象,還需要創(chuàng)建一個 PS 對象区拳,并重寫 getProperty 方法拘领,同時,注意:自己的 PS 配置優(yōu)先級需要高于容器配置文件的優(yōu)先級樱调,保險起見约素,放在第一位。

PS 構(gòu)造方法的第一個參數(shù)沒什么用笆凌,就是一個標(biāo)識符圣猎,第二個參數(shù)就是 source,可以定義為任何類型乞而,String送悔,Map,都可以爪模,我們這里簡單期間欠啤,就是一個 String,直接返回這個值屋灌,如果是 Map洁段,就調(diào)用 Map 的 get 方法。

為什么要實現(xiàn) BeanFactoryPostProcessor 接口呢共郭? 實現(xiàn) BeanFactoryPostProcessor 接口的目的是讓該 Bean 的加載時機(jī)提前祠丝,高于目標(biāo) Bean 的初始化疾呻。否則,目標(biāo) Bean 中的 timeout 屬性都注入結(jié)束了写半,后面的操作就沒有意義了岸蜗。

總結(jié)

說白了,就是不想寫配置文件5Aг馈!

而且也不想改老項目的代碼蟆肆,老項目即使在刪除配置文件的情況下矾睦,依然能夠使用配置中心!

這就需要熟悉 Spring 的配置加載邏輯和屬性獲取邏輯炎功。

現(xiàn)在枚冗,我們知道,只需要拿到 Spirng 的環(huán)境對象蛇损,并向環(huán)境中添加自定義的 PS 對象赁温,重寫 PS 的 getProperty 方法,即可獲取配置(注意優(yōu)先級)淤齐。

還需要注意加載這個配置的 bean 的優(yōu)先級也要很高股囊,通常實現(xiàn) BeanFactoryPostProcessor 接口就足夠了,如果還不夠更啄,就需要做一些特殊操作稚疹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市祭务,隨后出現(xiàn)的幾起案子内狗,更是在濱河造成了極大的恐慌,老刑警劉巖义锥,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柳沙,死亡現(xiàn)場離奇詭異,居然都是意外死亡拌倍,警方通過查閱死者的電腦和手機(jī)赂鲤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柱恤,“玉大人数初,你說我怎么就攤上這事」K常” “怎么了妙真?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荚守。 經(jīng)常有香客問我珍德,道長,這世上最難降的妖魔是什么矗漾? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任锈候,我火速辦了婚禮,結(jié)果婚禮上敞贡,老公的妹妹穿的比我還像新娘泵琳。我一直安慰自己,他們只是感情好誊役,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布获列。 她就那樣靜靜地躺著,像睡著了一般蛔垢。 火紅的嫁衣襯著肌膚如雪击孩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天鹏漆,我揣著相機(jī)與錄音巩梢,去河邊找鬼。 笑死艺玲,一個胖子當(dāng)著我的面吹牛括蝠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饭聚,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼忌警,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秒梳?” 一聲冷哼從身側(cè)響起法绵,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎端幼,沒想到半個月后礼烈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡婆跑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年此熬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滑进。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡犀忱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扶关,到底是詐尸還是另有隱情阴汇,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布节槐,位于F島的核電站搀庶,受9級特大地震影響拐纱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哥倔,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一秸架、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咆蒿,春花似錦东抹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒂破,卻和暖如春馏谨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寞蚌。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工田巴, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挟秤。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓壹哺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親艘刚。 傳聞我的和親對象是個殘疾皇子管宵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)攀甚,斷路器箩朴,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,721評論 2 22
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,838評論 6 342
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,168評論 2 7
  • 8月12日秋度,桑拿天炸庞,上午悶熱,中午暴雨荚斯,晚間天黃地黃埠居,大雨將至 科教興國,人才強(qiáng)國
    人從眾他閱讀 57評論 0 0