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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

javascript

SpringBoot源码解析(七)EnvironmentPostProcessor

發(fā)布時(shí)間:2024/3/26 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot源码解析(七)EnvironmentPostProcessor 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文將重點(diǎn)介紹SpringBoot提供給我們的另一個(gè)擴(kuò)展點(diǎn)EnvironmentPostProcessor,它允許我們到任意的指定目錄、以任意的方式加載一組配置,并賦予任意的優(yōu)先級(jí)

上文對(duì)prepareEnvironment方法的configureEnvironment做了一個(gè)收尾,本文繼續(xù)看第三行代碼listeners.environmentPrepared

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {ConfigurableEnvironment environment = this.getOrCreateEnvironment();this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());listeners.environmentPrepared((ConfigurableEnvironment)environment);this.bindToSpringApplication((ConfigurableEnvironment)environment);if (!this.isCustomEnvironment) {environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());}ConfigurationPropertySources.attach((Environment)environment);return (ConfigurableEnvironment)environment;}

這個(gè)listeners看過(guò)前文的朋友應(yīng)該還有印象,里面只有一個(gè)元素EventPublishingRunListener

public void environmentPrepared(ConfigurableEnvironment environment) {Iterator var2 = this.listeners.iterator();while(var2.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();listener.environmentPrepared(environment);}}

所以就相當(dāng)于調(diào)用了EventPublishingRunListener的environmentPrepared方法,查看該方法的實(shí)現(xiàn)

public void environmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}

通過(guò)內(nèi)部的事件多播器發(fā)布了一個(gè)事件ApplicationEnvironmentPreparedEvent,同時(shí)將SringApplication和Environment對(duì)象傳播了出去,發(fā)布的流程跟之前的ApplicationStartingEvent事件如出一轍,不再贅述

本次捕捉到該事件的監(jiān)聽(tīng)器共有7個(gè)

其中大多數(shù)監(jiān)聽(tīng)器是做一些邊邊角角的初始化工作,本文就先聚焦到其中一個(gè)比較重要的監(jiān)聽(tīng)器ConfigFileApplicationListener,它完成了對(duì)配置文件的加載,并且引入了本文要講的EnvironmentPostProcessor,它也是SpringBoot提供給我們的又一個(gè)擴(kuò)展點(diǎn)

直接進(jìn)入ConfigFileApplicationListener接收事件的方法onApplicationEvent

public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);}if (event instanceof ApplicationPreparedEvent) {this.onApplicationPreparedEvent(event);}}

事件類型為ApplicationEnvironmentPreparedEvent,走第一個(gè)分支

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);Iterator var3 = postProcessors.iterator();while(var3.hasNext()) {EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}}

第一行l(wèi)oadPostProcessors到META-INF/spring.factories中加載了EnvironmentPostProcessor的實(shí)現(xiàn)類,加載方式與SpringApplication初始化時(shí)的流程相似,雖然調(diào)用了不同的API,但最終都通過(guò)SpringFactoriesLoader.loadFactoryNames獲取了spring.factories的內(nèi)容

List<EnvironmentPostProcessor> loadPostProcessors() {return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, this.getClass().getClassLoader());}

最終找到三個(gè)類,都在spring-boot下的spring.factories中

# Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

回到onApplicationEnvironmentPreparedEvent方法,加載到EnvironmentPostProcessor的列表后,ConfigFileApplicationListener把自己也加入到了這個(gè)集合中,因?yàn)樗旧硪矊?shí)現(xiàn)了EnvironmentPostProcessor接口

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {

然后遍歷EnvironmentPostProcessor列表,依次調(diào)用其postProcessEnvironment方法,傳入Environment和SpringApplication

我們依次看下每個(gè)EnvironmentPostProcessor的實(shí)現(xiàn)類都做了什么事情

SpringApplicationJsonEnvironmentPostProcessor

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {MutablePropertySources propertySources = environment.getPropertySources();propertySources.stream().map(SpringApplicationJsonEnvironmentPostProcessor.JsonPropertyValue::get).filter(Objects::nonNull).findFirst().ifPresent((v) -> {this.processJson(environment, v);});}

先遍歷Environment中的PropertySource,依次傳給內(nèi)部類JsonPropertyValue的靜態(tài)方法get

public static SpringApplicationJsonEnvironmentPostProcessor.JsonPropertyValue get(PropertySource<?> propertySource) {String[] var1 = CANDIDATES;int var2 = var1.length;for(int var3 = 0; var3 < var2; ++var3) {String candidate = var1[var3];Object value = propertySource.getProperty(candidate);if (value != null && value instanceof String && StringUtils.hasLength((String)value)) {return new SpringApplicationJsonEnvironmentPostProcessor.JsonPropertyValue(propertySource, candidate, (String)value);}}return null;}

CANDIDATES是final變量,聲明時(shí)做了初始化

private static class JsonPropertyValue {private static final String[] CANDIDATES = new String[]{"spring.application.json", "SPRING_APPLICATION_JSON"};

也就是針對(duì)每個(gè)PropertySource,看里面有沒(méi)有屬性spring.application.json或者SPRING_APPLICATION_JSON,如果有的話,把它轉(zhuǎn)化為JsonPropertyValue存儲(chǔ)起來(lái),然后取第一個(gè),調(diào)用processJson方法

private void processJson(ConfigurableEnvironment environment, SpringApplicationJsonEnvironmentPostProcessor.JsonPropertyValue propertyValue) {JsonParser parser = JsonParserFactory.getJsonParser();Map<String, Object> map = parser.parseMap(propertyValue.getJson());if (!map.isEmpty()) {this.addJsonPropertySource(environment, new SpringApplicationJsonEnvironmentPostProcessor.JsonPropertySource(propertyValue, this.flatten(map)));}}

這個(gè)方法里把找到的屬性對(duì)應(yīng)的value轉(zhuǎn)化為map,然后構(gòu)造了一個(gè)JsonPropertySource,它是MapPropertySource的子類,配置的組名為spring.application.json

JsonPropertySource(SpringApplicationJsonEnvironmentPostProcessor.JsonPropertyValue propertyValue, Map<String, Object> source) {super("spring.application.json", source);this.propertyValue = propertyValue;}

然后添加到Environemtn的PropertySource列表,優(yōu)先級(jí)是如果列表中有jndiProperties就放在它后面,如果沒(méi)有就放在systemProperties,如果都沒(méi)有,就放在列表頭部,作為優(yōu)先級(jí)最高的配置

private void addJsonPropertySource(ConfigurableEnvironment environment, PropertySource<?> source) {MutablePropertySources sources = environment.getPropertySources();String name = this.findPropertySource(sources);if (sources.contains(name)) {sources.addBefore(name, source);} else {sources.addFirst(source);}} private String findPropertySource(MutablePropertySources sources) {return ClassUtils.isPresent("org.springframework.web.context.support.StandardServletEnvironment", (ClassLoader)null) && sources.contains("jndiProperties") ? "jndiProperties" : "systemProperties";}

SystemEnvironmentPropertySourceEnvironmentPostProcessor

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {String sourceName = "systemEnvironment";PropertySource<?> propertySource = environment.getPropertySources().get(sourceName);if (propertySource != null) {this.replacePropertySource(environment, sourceName, propertySource);}}

先從Environment中取了名為systemEnvironment的PropertySource,也就是系統(tǒng)的環(huán)境變量,然后調(diào)用replacePropertySource對(duì)它做了一個(gè)包裝替換

private void replacePropertySource(ConfigurableEnvironment environment, String sourceName, PropertySource<?> propertySource) {Map<String, Object> originalSource = (Map)propertySource.getSource();SystemEnvironmentPropertySource source = new SystemEnvironmentPropertySourceEnvironmentPostProcessor.OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource);environment.getPropertySources().replace(sourceName, source);}

systemEnvironment原先的具體類型為SystemEnvironmentPropertySource,這里替換的類型OriginAwareSystemEnvironmentPropertySource是它的子類,它額外實(shí)現(xiàn)了OriginLookup接口

所以SystemEnvironmentPropertySourceEnvironmentPostProcessor就是對(duì)系統(tǒng)環(huán)境變量做了一個(gè)包裝,不影響原有的功能

CloudFoundryVcapEnvironmentPostProcessor

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {Properties properties = new Properties();JsonParser jsonParser = JsonParserFactory.getJsonParser();this.addWithPrefix(properties, this.getPropertiesFromApplication(environment, jsonParser), "vcap.application.");this.addWithPrefix(properties, this.getPropertiesFromServices(environment, jsonParser), "vcap.services.");MutablePropertySources propertySources = environment.getPropertySources();if (propertySources.contains("commandLineArgs")) {propertySources.addAfter("commandLineArgs", new PropertiesPropertySource("vcap", properties));} else {propertySources.addFirst(new PropertiesPropertySource("vcap", properties));}}} CLOUD_FOUNDRY {public boolean isActive(Environment environment) {return environment.containsProperty("VCAP_APPLICATION") || environment.containsProperty("VCAP_SERVICES");}},

這個(gè)postProcessor主要針對(duì)引入了CloudFound的項(xiàng)目,如果配置了VCAP_APPLICATION或者VCAP_SERVICES屬性,就往Environment的PropertySource列表添加名為vcap的配置

CloudFound這東西我也沒(méi)見(jiàn)有項(xiàng)目用過(guò),它是一個(gè)開(kāi)源的PAAS,據(jù)說(shuō)是業(yè)界最早的開(kāi)源云平臺(tái),不過(guò)就跟之前提到的Liquibase一樣,雖然被SpringBoot默認(rèn)支持了,但是至少在國(guó)內(nèi)的受眾還是比較少的

ConfigFileApplicationListener

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {this.addPropertySources(environment, application.getResourceLoader());}

參數(shù)中傳遞的resourceLoader可以將一個(gè)指定位置的文件抽象成Resource,供后續(xù)解析使用,不過(guò)這里還是空的,因?yàn)镾pringApplication創(chuàng)建的時(shí)候并沒(méi)有對(duì)該屬性作初始化

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {RandomValuePropertySource.addToEnvironment(environment);(new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();}

第一行代碼往Environment中添加了一個(gè)名為random的PropertySource

public static void addToEnvironment(ConfigurableEnvironment environment) {environment.getPropertySources().addAfter("systemEnvironment", new RandomValuePropertySource("random"));logger.trace("RandomValuePropertySource add to Environment");}

類型為RandomValuePropertySource,存儲(chǔ)資源的類型指定為Random

public class RandomValuePropertySource extends PropertySource<Random> {public static final String RANDOM_PROPERTY_SOURCE_NAME = "random";private static final String PREFIX = "random.";private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class);public RandomValuePropertySource(String name) {super(name, new Random());}

其作用是為配置文件中的部分屬性提供隨機(jī)數(shù)功能,比如我們想讓系統(tǒng)運(yùn)行在5000-6000內(nèi)的隨機(jī)端口上,就可以添加如下配置

server:port: ${random.int[5000,6000]}

至于后面的一行代碼就是真正去加載系統(tǒng)的配置文件了,我們后面再單獨(dú)開(kāi)一篇來(lái)討論

自定義EnvironmentPostProcessor

通過(guò)上述幾個(gè)內(nèi)置的EnvironmentPostProcessor,我們大致了解了這個(gè)接口的作用,通過(guò)自定義這個(gè)接口的實(shí)現(xiàn),可以對(duì)Environment為所欲為,甚至改變系統(tǒng)的默認(rèn)配置,先做個(gè)實(shí)驗(yàn),自定義一個(gè)EnvironmentPostProcessor,把上面添加的RandomValuePropertySource刪掉,讓系統(tǒng)失去對(duì)配置文件的隨機(jī)數(shù)支持

首先正常啟動(dòng)項(xiàng)目,可以看到端口在5000-6000中隨機(jī)產(chǎn)生

新建類MyEnvironmentPostProcessor實(shí)現(xiàn)EnvironmentPostProcessor 接口,將名為random的PropertySource從environment中刪除

@Order(-2147483637) public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {MutablePropertySources propertySources = environment.getPropertySources();propertySources.remove("random");} }

需要注意的一點(diǎn)是,EnvironmentPostProcessor 是一個(gè)列表,會(huì)按一定順序執(zhí)行,我們要在ConfigFileApplicationListener將random添加到environment之后刪除才有效果

ConfigFileApplicationListener實(shí)現(xiàn)了Ordered 接口,并指定order為-2147483638

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {......private int order = -2147483638;......public int getOrder() {return this.order;}......

order值越小的越先執(zhí)行,所以我們自己的類加上@Order注解,order設(shè)置為-2147483637,只要比ConfigFileApplicationListener 大就行

再次運(yùn)行拋出異常,無(wú)法為端口生成隨機(jī)數(shù)

當(dāng)然這個(gè)例子只是說(shuō)明EnvironmentPostProcessor給了我們很高的權(quán)限,去干預(yù)正常啟動(dòng)過(guò)程中的環(huán)境配置,實(shí)際中我們可以借助它來(lái)做一些公用配置的處理

假如我們有A B C三個(gè)服務(wù),共同依賴了底層模塊commom,想把一些公用的配置放在common模塊中,并擁有最高的優(yōu)先級(jí),就可以在common新建一個(gè)配置文件application-common.properties,然后定義一個(gè)實(shí)現(xiàn)類CommonEnvironmentPostProcessor,去解析這個(gè)文件,并加載到PropertySource列表首部,當(dāng)然還要注意如果想讓配置的優(yōu)先級(jí)最高,還要盡可能保證它最晚執(zhí)行,所以order要設(shè)置的比較大

@Order(Integer.99999) @Slf4j public class CommonEnvironmentPostProcessor implements EnvironmentPostProcessor {@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {try {//定義一個(gè)資源加載器ResourceLoader resourceLoader = new DefaultResourceLoader();//找到classpath下的application-common.properties文件,抽象成ResourceResource resource = resourceLoader.getResource("classpath:/application-common.properties");if (!resource.exists()) {return;}Properties properties = new Properties();//從文件中裝載配置properties.load(new InputStreamReader(resource.getInputStream()));PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("commonProperties", properties);//獲取environment中的配置列表MutablePropertySources mutablePropertySources = environment.getPropertySources();//將當(dāng)前配置添加到列表首部,即擁有最高的優(yōu)先級(jí)mutablePropertySources.addFirst(propertiesPropertySource);} catch (Exception e) {log.error("加載公共配置失敗", e);}}}

總結(jié)

以上是生活随笔為你收集整理的SpringBoot源码解析(七)EnvironmentPostProcessor的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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