您好,登錄后才能下訂單哦!
這篇文章主要講解了“Spring Cloud中怎么自定義外部化擴展機制”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Spring Cloud中怎么自定義外部化擴展機制”吧!
Spring Cloud針對Environment的屬性源功能做了增強,
在spring-cloud-contenxt這個包中,提供了PropertySourceLocator接口,用來實現(xiàn)屬性文件加載的擴展。我們可以通過這個接口來擴展自己的外部化配置加載。這個接口的定義如下
public interface PropertySourceLocator { /** * @param environment The current Environment. * @return A PropertySource, or null if there is none. * @throws IllegalStateException if there is a fail-fast condition. */ PropertySource<?> locate(Environment environment); }
locate這個抽象方法,需要返回一個PropertySource對象。
這個PropertySource就是Environment中存儲的屬性源。 也就是說,我們?nèi)绻獙崿F(xiàn)自定義外部化配置加載,只需要實現(xiàn)這個接口并返回PropertySource即可。
按照這個思路,我們按照下面幾個步驟來實現(xiàn)外部化配置的自定義加載。
既然PropertySourceLocator需要返回一個PropertySource,那我們必須要定義一個自己的PropertySource,來從外部獲取配置來源。
GpDefineMapPropertySource 表示一個以Map結(jié)果作為屬性來源的類。
public class GpDefineMapPropertySource extends MapPropertySource { /** * Create a new {@code MapPropertySource} with the given name and {@code Map}. * * @param name the associated name * @param source the Map source (without {@code null} values in order to get * consistent {@link #getProperty} and {@link #containsProperty} behavior) */ public GpDefineMapPropertySource(String name, Map<String, Object> source) { super(name, source); } @Override public Object getProperty(String name) { return super.getProperty(name); public String[] getPropertyNames() { return super.getPropertyNames(); }
擴展PropertySourceLocator,重寫locate提供屬性源。
而屬性源是從gupao.json文件加載保存到自定義屬性源GpDefineMapPropertySource中。
public class GpJsonPropertySourceLocator implements PropertySourceLocator { //json數(shù)據(jù)來源 private final static String DEFAULT_LOCATION="classpath:gupao.json"; //資源加載器 private final ResourceLoader resourceLoader=new DefaultResourceLoader(getClass().getClassLoader()); @Override public PropertySource<?> locate(Environment environment) { //設(shè)置屬性來源 GpDefineMapPropertySource jsonPropertySource=new GpDefineMapPropertySource ("gpJsonConfig",mapPropertySource()); return jsonPropertySource; } private Map<String,Object> mapPropertySource(){ Resource resource=this.resourceLoader.getResource(DEFAULT_LOCATION); if(resource==null){ return null; } Map<String,Object> result=new HashMap<>(); JsonParser parser= JsonParserFactory.getJsonParser(); Map<String,Object> fileMap=parser.parseMap(readFile(resource)); processNestMap("",result,fileMap); return result; //加載文件并解析 private String readFile(Resource resource){ FileInputStream fileInputStream=null; try { fileInputStream=new FileInputStream(resource.getFile()); byte[] readByte=new byte[(int)resource.getFile().length()]; fileInputStream.read(readByte); return new String(readByte,"UTF-8"); } catch (IOException e) { e.printStackTrace(); }finally { if(fileInputStream!=null){ try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } return null; //解析完整的url保存到result集合。 為了實現(xiàn)@Value注入 private void processNestMap(String prefix,Map<String,Object> result,Map<String,Object> fileMap){ if(prefix.length()>0){ prefix+="."; for (Map.Entry<String, Object> entrySet : fileMap.entrySet()) { if (entrySet.getValue() instanceof Map) { processNestMap(prefix + entrySet.getKey(), result, (Map<String, Object>) entrySet.getValue()); } else { result.put(prefix + entrySet.getKey(), entrySet.getValue()); }
在/META-INF/spring.factories
文件中,添加下面的spi擴展,讓Spring Cloud啟動時掃描到這個擴展從而實現(xiàn)GpJsonPropertySourceLocator的加載。
org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.gupaoedu.env.GpJsonPropertySourceLocator
@RestController public class ConfigController { @Value("${custom.property.message}") private String name; @GetMapping("/") public String get(){ String msg=String.format("配置值:%s",name); return msg; } }
通過上述案例可以發(fā)現(xiàn),基于Spring Boot提供的PropertySourceLocator擴展機制,可以輕松實現(xiàn)自定義配置源的擴展。
于是,引出了兩個問題。
PropertySourceLocator是在哪個被觸發(fā)的?
既然能夠從gupao.json
中加載數(shù)據(jù)源,是否能從遠程服務(wù)器上加載呢?
先來探索第一個問題,PropertySourceLocator的執(zhí)行流程。
在spring boot項目啟動時,有一個prepareContext的方法,它會回調(diào)所有實現(xiàn)了ApplicationContextInitializer
的實例,來做一些初始化工作。
ApplicationContextInitializer是Spring框架原有的東西, 它的主要作用就是在,ConfigurableApplicationContext類型(或者子類型)的ApplicationContext做refresh之前,允許我們對ConfiurableApplicationContext的實例做進一步的設(shè)置和處理。
它可以用在需要對應(yīng)用程序上下文進行編程初始化的web應(yīng)用程序中,比如根據(jù)上下文環(huán)境來注冊propertySource,或者配置文件。而Config 的這個配置中心的需求恰好需要這樣一個機制來完成。
public ConfigurableApplicationContext run(String... args) { //省略代碼... prepareContext(context, environment, listeners, applicationArguments, printedBanner); //省略代碼 return context; }
其中,PropertySourceBootstrapConfiguration就實現(xiàn)了ApplicationContextInitializer
,initialize
方法代碼如下。
@Override public void initialize(ConfigurableApplicationContext applicationContext) { List<PropertySource<?>> composite = new ArrayList<>(); //對propertySourceLocators數(shù)組進行排序,根據(jù)默認的AnnotationAwareOrderComparator AnnotationAwareOrderComparator.sort(this.propertySourceLocators); boolean empty = true; //獲取運行的環(huán)境上下文 ConfigurableEnvironment environment = applicationContext.getEnvironment(); for (PropertySourceLocator locator : this.propertySourceLocators) { //回調(diào)所有實現(xiàn)PropertySourceLocator接口實例的locate方法,并收集到source這個集合中。 Collection<PropertySource<?>> source = locator.locateCollection(environment); if (source == null || source.size() == 0) { //如果source為空,直接進入下一次循環(huán) continue; } //遍歷source,把PropertySource包裝成BootstrapPropertySource加入到sourceList中。 List<PropertySource<?>> sourceList = new ArrayList<>(); for (PropertySource<?> p : source) { sourceList.add(new BootstrapPropertySource<>(p)); } logger.info("Located property source: " + sourceList); composite.addAll(sourceList);//將source添加到數(shù)組 empty = false; //表示propertysource不為空 } //只有propertysource不為空的情況,才會設(shè)置到environment中 if (!empty) { //獲取當前Environment中的所有PropertySources. MutablePropertySources propertySources = environment.getPropertySources(); String logConfig = environment.resolvePlaceholders("${logging.config:}"); LogFile logFile = LogFile.get(environment); // 遍歷移除bootstrapProperty的相關(guān)屬性 for (PropertySource<?> p : environment.getPropertySources()) { if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { propertySources.remove(p.getName()); } } //把前面獲取到的PropertySource,插入到Environment中的PropertySources中。 insertPropertySources(propertySources, composite); reinitializeLoggingSystem(environment, logConfig, logFile); setLogLevels(applicationContext, environment); handleIncludedProfiles(environment); } }
上述代碼邏輯說明如下。
1.首先this.propertySourceLocators
,表示所有實現(xiàn)了PropertySourceLocators
接口的實現(xiàn)類,其中就包括我們前面自定義的GpJsonPropertySourceLocator
。
2.根據(jù)默認的 AnnotationAwareOrderComparator 排序規(guī)則對propertySourceLocators數(shù)組進行排序。
3.獲取運行的環(huán)境上下文ConfigurableEnvironment
4.遍歷propertySourceLocators時
調(diào)用 locate 方法,傳入獲取的上下文environment
將source添加到PropertySource的鏈表中
設(shè)置source是否為空的標識標量empty
5.source不為空的情況,才會設(shè)置到environment中返回Environment的可變形式,可進行的操作如addFirst、addLast移除propertySources中的bootstrapProperties根據(jù)config server覆寫的規(guī)則,設(shè)置propertySources處理多個active profiles的配置信息
返回Environment的可變形式,可進行的操作如addFirst、addLast
移除propertySources中的bootstrapProperties
根據(jù)config server覆寫的規(guī)則,設(shè)置propertySources
處理多個active profiles的配置信息
注意:this.propertySourceLocators這個集合中的PropertySourceLocator,是通過自動裝配機制完成注入的,具體的實現(xiàn)在BootstrapImportSelector
這個類中。
ApplicationContextInitializer是Spring框架原有的東西, 它的主要作用就是在,ConfigurableApplicationContext類型(或者子類型)的ApplicationContext做refresh之前,允許我們對ConfiurableApplicationContext的實例做進一步的設(shè)置和處理。
它可以用在需要對應(yīng)用程序上下文進行編程初始化的web應(yīng)用程序中,比如根據(jù)上下文環(huán)境來注冊propertySource,或者配置文件。而Config 的這個配置中心的需求恰好需要這樣一個機制來完成。
public class TestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{ @Override public void initialize(ConfigurableApplicationContext applicationContext) { ConfigurableEnvironment ce=applicationContext.getEnvironment(); for(PropertySource<?> propertySource:ce.getPropertySources()){ System.out.println(propertySource); } System.out.println("--------end"); } }
創(chuàng)建一個文件/resources/META-INF/spring.factories。添加如下內(nèi)容
org.springframework.context.ApplicationContextInitializer= \ com.gupaoedu.example.springcloudconfigserver9091.TestApplicationContextInitializer
在控制臺就可以看到當前的PropertySource的輸出結(jié)果。
ConfigurationPropertySourcesPropertySource {name='configurationProperties'} StubPropertySource {name='servletConfigInitParams'} StubPropertySource {name='servletContextInitParams'} PropertiesPropertySource {name='systemProperties'} OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'} RandomValuePropertySource {name='random'} MapPropertySource {name='configServerClient'} MapPropertySource {name='springCloudClientHostInfo'} OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'} MapPropertySource {name='kafkaBinderDefaultProperties'} MapPropertySource {name='defaultProperties'} MapPropertySource {name='springCloudDefaultProperties'}
感謝各位的閱讀,以上就是“Spring Cloud中怎么自定義外部化擴展機制”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Spring Cloud中怎么自定義外部化擴展機制這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。