接著之前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é)果
我們啟動容器邓深,查看控制臺輸出
這里我們注意到未桥,我們的測試的值已經(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é)果
執(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é)果
不出意外泌霍,我們也同樣改變了擴(kuò)展的值。
小結(jié)
對于外部化配置的方面述召,相信在之后的開發(fā)中會有很大的用途朱转,特別是各種整合蟹地。希望在接下來的實際開發(fā)中,我們可以用到這些實現(xiàn)藤为。