欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot外部化配置實戰(zhàn)解析

 更新時間:2019年06月04日 10:24:12   作者:宜信技術(shù)  
這篇文章主要介紹了Spring Boot外部化配置實戰(zhàn)解析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

一、流程分析

1.1 入口程序

在 SpringApplication#run(String... args) 方法中,外部化配置關(guān)鍵流程分為以下四步

public ConfigurableApplicationContext 

run(String... args) {

  ...

  SpringApplicationRunListeners listeners = getRunListeners(args); // 1

  listeners.starting();

  try {

    ApplicationArguments applicationArguments = new DefaultApplicationArguments(

      args);

    ConfigurableEnvironment environment = prepareEnvironment(listeners,

                                 applicationArguments); // 2

    configureIgnoreBeanInfo(environment);

    Banner printedBanner = printBanner(environment);

    context = createApplicationContext();

    exceptionReporters = getSpringFactoriesInstances(

      SpringBootExceptionReporter.class,

      new Class[] { ConfigurableApplicationContext.class }, context);

    prepareContext(context, environment, listeners, applicationArguments,

            printedBanner); // 3

    refreshContext(context); // 4

    afterRefresh(context, applicationArguments);

    stopWatch.stop();

    if (this.logStartupInfo) {

      new StartupInfoLogger(this.mainApplicationClass)

        .logStarted(getApplicationLog(), stopWatch);

    }

    listeners.started(context);

    callRunners(context, applicationArguments);

  }

  ...

}

1.2 關(guān)鍵流程思維導圖

1.3 關(guān)鍵流程詳解

對入口程序中標記的四步,分析如下

1.3.1 SpringApplication#getRunListeners

加載 META-INF/spring.factories

獲取 SpringApplicationRunListener

的實例集合,存放的對象是 EventPublishingRunListener 類型 以及自定義的 SpringApplicationRunListener 實現(xiàn)類型

1.3.2 SpringApplication#prepareEnvironment

prepareEnvironment 方法中,主要的三步如下

private ConfigurableEnvironment 

prepareEnvironment(SpringApplicationRunListeners listeners,

  ApplicationArguments applicationArguments) {

  // Create and configure the environment

  ConfigurableEnvironment environment = getOrCreateEnvironment(); // 2.1

  configureEnvironment(environment, applicationArguments.getSourceArgs()); // 2.2

  listeners.environmentPrepared(environment); // 2.3

  ...

  return environment;

}

1) getOrCreateEnvironment 方法

在 WebApplicationType.SERVLET web應用類型下,會創(chuàng)建 StandardServletEnvironment,本文以 StandardServletEnvironment 為例,類的層次結(jié)構(gòu)如下

當創(chuàng)建 StandardServletEnvironment,StandardServletEnvironment 父類 AbstractEnvironment 調(diào)用 customizePropertySources 方法,會執(zhí)行 StandardServletEnvironment#customizePropertySources和 StandardEnvironment#customizePropertySources ,源碼如下AbstractEnvironment

public AbstractEnvironment() {

  customizePropertySources(this.propertySources);

  if (logger.isDebugEnabled()) {

    logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources);

  }

}

StandardServletEnvironment#customizePropertySources

/** Servlet context init parameters property source name: {@value} */

public static final 

StringSERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

/** Servlet config init parameters property source name: {@value} */

public static final String 

SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

/** JNDI property source name: {@value} */

public static final String 

JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

@Override

protected void customizePropertySources(MutablePropertySources propertySources) {

  propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));

  propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));

  if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {

    propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));

  }

  super.customizePropertySources(propertySources);

}

StandardEnvironment#customizePropertySources

/** System environment property source name: {@value} */

public static final String 

SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

/** JVM system properties property source name: {@value} */

public static final String 

SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

@Override

protected void customizePropertySources(MutablePropertySources propertySources) {

  propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));

  propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,getSystemEnvironment());

}

PropertySources 順序:

  • servletConfigInitParams
  • servletContextInitParams
  • jndiProperties
  • systemProperties
  • systemEnvironment

PropertySources 與 PropertySource 關(guān)系為 1 對 N

2) configureEnvironment 方法

調(diào)用 configurePropertySources(environment, args), 在方法里面設(shè)置 Environment 的 PropertySources , 包含 defaultProperties 和

SimpleCommandLinePropertySource(commandLineArgs),PropertySources 添加 defaultProperties 到最后,添加

SimpleCommandLinePropertySource(commandLineArgs)到最前面

PropertySources 順序:

  • commandLineArgs
  • servletConfigInitParams
  • servletContextInitParams
  • jndiProperties
  • systemProperties
  • systemEnvironment
  • defaultProperties

3) listeners.environmentPrepared 方法

會按優(yōu)先級順序遍歷執(zhí)行 SpringApplicationRunListener#environmentPrepared,比如 EventPublishingRunListener 和 自定義的 SpringApplicationRunListener

EventPublishingRunListener 發(fā)布

ApplicationEnvironmentPreparedEvent 事件

ConfigFileApplicationListener 監(jiān)聽

ApplicationEvent 事件 、處理 ApplicationEnvironmentPreparedEvent 事件,加載所有 EnvironmentPostProcessor 包括自己,然后按照順序進行方法回調(diào)

---ConfigFileApplicationListener#postProcessEnvironment方法回調(diào) ,然后addPropertySources 方法調(diào)用

RandomValuePropertySource#addToEnvironment,在 systemEnvironment 后面添加 random,然后添加配置文件的屬性源(詳見源碼ConfigFileApplicationListener.Loader#load()

擴展點

  • 自定義 SpringApplicationRunListener ,重寫 environmentPrepared 方法
  • 自定義 EnvironmentPostProcessor
  • 自定義 ApplicationListener 監(jiān)聽 ApplicationEnvironmentPreparedEvent 事件
  • ConfigFileApplicationListener,即是 EnvironmentPostProcessor ,又是 ApplicationListener ,類的層次結(jié)構(gòu)如下

@Override

public void onApplicationEvent(ApplicationEvent event) {

  // 處理 ApplicationEnvironmentPreparedEvent 事件

  if (event instanceof ApplicationEnvironmentPreparedEvent) {

    onApplicationEnvironmentPreparedEvent(

      (ApplicationEnvironmentPreparedEvent) event);

  }

  // 處理 ApplicationPreparedEvent 事件

  if (event instanceof ApplicationPreparedEvent) {

    onApplicationPreparedEvent(event);

  }

}

private void onApplicationEnvironmentPreparedEvent(

  ApplicationEnvironmentPreparedEvent event) {

  // 加載 META-INF/spring.factories 中配置的 EnvironmentPostProcessor

  List

  // 加載自己 ConfigFileApplicationListener

  postProcessors.add(this);

  // 按照 Ordered 進行優(yōu)先級排序

  AnnotationAwareOrderComparator.sort(postProcessors);

  // 回調(diào) EnvironmentPostProcessor

  for (EnvironmentPostProcessor postProcessor : postProcessors) {

    postProcessor.postProcessEnvironment(event.getEnvironment(),                      event.getSpringApplication());

  }

}

List

  return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,                        getClass().getClassLoader());

}

@Override

public void 

postProcessEnvironment(ConfigurableEnvironment environment,

                  SpringApplication application) {

  addPropertySources(environment, application.getResourceLoader());

}

/**

 * Add config file property sources to the specified environment.

 * @param environment the environment to add source to

 * @param resourceLoader the resource loader

 * @see 

#addPostProcessors(ConfigurableApplicationContext)

 */

protected void 

addPropertySources(ConfigurableEnvironment environment,

                 ResourceLoader resourceLoader) {

RandomValuePropertySource.addToEnvironment(environment);

  // 添加配置文件的屬性源

  new Loader(environment, resourceLoader).load();

}

RandomValuePropertySource

public static void 

addToEnvironment(ConfigurableEnvironment environment) {

  // 在 systemEnvironment 后面添加 random

  environment.getPropertySources().addAfter(

    StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,

    new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));

  logger.trace("RandomValuePropertySource add to Environment");

}

添加配置文件的屬性源:執(zhí)行

new Loader(environment, resourceLoader).load();,

調(diào)用 load(Profile, DocumentFilterFactory, DocumentConsumer)(getSearchLocations()

獲取配置文件位置,可以指定通過 spring.config.additional-location 、spring.config.location 、spring.config.name 參數(shù)或者使用默認值 ), 然后調(diào)用 addLoadedPropertySources -> addLoadedPropertySource(加載 查找出來的 PropertySource 到 PropertySources,并確保放置到 defaultProperties 的前面 )

默認的查找位置,配置為

"classpath:/,classpath:/config/,file:./,file:./config/",查找順序從后向前

PropertySources 順序:

  • commandLineArgs
  • servletConfigInitParams
  • servletContextInitParams
  • jndiProperties
  • systemProperties
  • systemEnvironment
  • random
  • application.properties ...
  • defaultProperties

1.3.3 SpringApplication#prepareContext

prepareContext 方法中,主要的三步如下

private void 

prepareContext(ConfigurableApplicationContext context,

              ConfigurableEnvironment environment,

              SpringApplicationRunListeners listeners,

              ApplicationArguments applicationArguments,

              Banner printedBanner) {

  ...

  applyInitializers(context); // 3.1

  listeners.contextPrepared(context); //3.2

  ...

  listeners.contextLoaded(context); // 3.3

}

1)applyInitializers 方法

會遍歷執(zhí)行所有的 ApplicationContextInitializer#initialize

擴展點

自定義 ApplicationContextInitializer

2)listeners.contextPrepared 方法

會按優(yōu)先級順序遍歷執(zhí)行 SpringApplicationRunListener#contextPrepared,比如 EventPublishingRunListener 和 自定義的 SpringApplicationRunListener

擴展點

自定義 SpringApplicationRunListener ,重寫 contextPrepared 方法

3)listeners.contextLoaded 方法

會按優(yōu)先級順序遍歷執(zhí)行 SpringApplicationRunListener#contextLoaded,比如 EventPublishingRunListener 和 自定義的 SpringApplicationRunListener

EventPublishingRunListener 發(fā)布

ApplicationPreparedEvent 事件

ConfigFileApplicationListener 監(jiān)聽

ApplicationEvent 事件 處理

ApplicationPreparedEvent 事件

擴展點

  • 自定義 SpringApplicationRunListener ,重寫 contextLoaded 方法
  • 自定義 ApplicationListener ,監(jiān)聽 ApplicationPreparedEvent 事件

ConfigFileApplicationListener

@Override

public void onApplicationEvent(ApplicationEvent event) {

  // 處理 ApplicationEnvironmentPreparedEvent 事件

  if (event instanceof 

ApplicationEnvironmentPreparedEvent) {

    onApplicationEnvironmentPreparedEvent(

      (ApplicationEnvironmentPreparedEvent) event);

  }

  // 處理 ApplicationPreparedEvent 事件

  if (event instanceof ApplicationPreparedEvent) {

    onApplicationPreparedEvent(event);

  }

}

private void onApplicationPreparedEvent(ApplicationEvent event) {

  this.logger.replayTo(ConfigFileApplicationListener.class);

  addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());

}

// 添加 PropertySourceOrderingPostProcessor 處理器,配置 PropertySources

protected void addPostProcessors(ConfigurableApplicationContext context) {

  context.addBeanFactoryPostProcessor(

    new PropertySourceOrderingPostProcessor(context));

}

PropertySourceOrderingPostProcessor

// 回調(diào)處理(在配置類屬性源解析)

@Override

public void 

postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

  throws BeansException {

  reorderSources(this.context.getEnvironment());

}

// 調(diào)整 PropertySources 順序,先刪除 defaultProperties, 再把 defaultProperties 添加到最后

private void reorderSources(ConfigurableEnvironment environment) {

  PropertySource

    .remove(DEFAULT_PROPERTIES);

  if (defaultProperties != null) {

    environment.getPropertySources().addLast(defaultProperties);

  }

}

PropertySourceOrderingPostProcessor 是 BeanFactoryPostProcessor

1.3.4 SpringApplication#refreshContext

會進行 @Configuration 配置類屬性源解析,處理 @PropertySource annotations on your @Configuration classes,但順序是在 defaultProperties 之后,下面會把defaultProperties 調(diào)整到最后

AbstractApplicationContext#refresh 調(diào)用 invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors), 然后進行 BeanFactoryPostProcessor 的回調(diào)處理 ,比如 PropertySourceOrderingPostProcessor 的回調(diào)(源碼見上文)

PropertySources 順序:

  • commandLineArgs
  • servletConfigInitParams
  • servletContextInitParams
  • jndiProperties
  • systemProperties
  • systemEnvironment
  • random
  • application.properties ...
  • @PropertySource annotations on your @Configuration classes
  • defaultProperties

(不推薦使用這種方式,推薦使用在 refreshContext 之前準備好,@PropertySource 加載太晚,不會對自動配置產(chǎn)生任何影響)

二、擴展外部化配置屬性源

2.1 基于 EnvironmentPostProcessor 擴展

public class CustomEnvironmentPostProcessor 

implements EnvironmentPostProcessor

2.2 基于 ApplicationEnvironmentPreparedEvent 擴展

public class 

ApplicationEnvironmentPreparedEventListener implements ApplicationListener

2.3 基于 SpringApplicationRunListener 擴展

public class CustomSpringApplicationRunListener implements SpringApplicationRunListener, Ordered

可以重寫方法 environmentPrepared、contextPrepared、contextLoaded 進行擴展

2.4 基于 ApplicationContextInitializer 擴展

public class CustomApplicationContextInitializer implements ApplicationContextInitializer

關(guān)于與 Spring Cloud Config Client 整合,對外部化配置加載的擴展(綁定到Config Server,使用遠端的property sources 初始化 Environment),參考源碼PropertySourceBootstrapConfiguration(是對 ApplicationContextInitializer 的擴展)、ConfigServicePropertySourceLocator#locate

獲取遠端的property sources是 RestTemplate 通過向 http://{spring.cloud.config.uri}/{spring.application.name}/{spring.cloud.config.profile}/{spring.cloud.config.label} 發(fā)送 GET 請求方式獲取的

2.5 基于 ApplicationPreparedEvent 擴展

public class ApplicationPreparedEventListener 

implements ApplicationListener

2.6 擴展實戰(zhàn)

2.6.1 擴展配置

在 classpath 下添加配置文件 META-INF/spring.factories, 內(nèi)容如下

# Spring Application Run Listeners

org.springframework.boot.SpringApplicationRunListener=\

springboot.propertysource.extend.listener.CustomSpringApplicationRunListener

# Application Context Initializers

org.springframework.context.ApplicationContextInitializer=\

springboot.propertysource.extend.initializer.CustomApplicationContextInitializer

# Application Listeners

org.springframework.context.ApplicationListener=\

springboot.propertysource.extend.event.listener.ApplicationEnvironmentPreparedEventListener,\

springboot.propertysource.extend.event.listener.ApplicationPreparedEventListener

# Environment Post Processors

org.springframework.boot.env.EnvironmentPostProcessor=\

springboot.propertysource.extend.processor.CustomEnvironmentPostProcessor

以上的擴展可以選取其中一種進行擴展,只是屬性源的加載時機不太一樣

2.6.2 擴展實例代碼

https://github.com/shijw823/springboot-externalized-configuration-extend.git

PropertySources 順序:

propertySourceName: [ApplicationPreparedEventListener], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [CustomSpringApplicationRunListener-contextLoaded], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [CustomSpringApplicationRunListener-contextPrepared], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [CustomApplicationContextInitializer], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [bootstrapProperties], propertySourceClassName: [CompositePropertySource]

propertySourceName: [configurationProperties], propertySourceClassName: [ConfigurationPropertySourcesPropertySource]

propertySourceName: [CustomSpringApplicationRunListener-environmentPrepared], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [CustomEnvironmentPostProcessor-dev-application], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [ApplicationEnvironmentPreparedEventListener], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [commandLineArgs], propertySourceClassName: [SimpleCommandLinePropertySource]

propertySourceName: [servletConfigInitParams], propertySourceClassName: [StubPropertySource]

propertySourceName: [servletContextInitParams], propertySourceClassName: [ServletContextPropertySource]

propertySourceName: [systemProperties], propertySourceClassName: [MapPropertySource]

propertySourceName: [systemEnvironment], propertySourceClassName: [OriginAwareSystemEnvironmentPropertySource]

propertySourceName: [random], propertySourceClassName: [RandomValuePropertySource]

propertySourceName: [applicationConfig: [classpath:/extend/config/springApplicationRunListener.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [applicationConfig: [classpath:/extend/config/applicationListener.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [applicationConfig: [classpath:/extend/config/applicationContextInitializer.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [applicationConfig: [classpath:/extend/config/environmentPostProcessor.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [applicationConfig: [classpath:/extend/config/application.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [applicationConfig: [classpath:/extend/config/config.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [applicationConfig: [classpath:/application.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [springCloudClientHostInfo], propertySourceClassName: [MapPropertySource]

propertySourceName: [applicationConfig: [classpath:/bootstrap.properties]], propertySourceClassName: [OriginTrackedMapPropertySource]

propertySourceName: [propertySourceConfig], propertySourceClassName: [ResourcePropertySource]

propertySourceName: [defaultProperties], propertySourceClassName: [MapPropertySource]

bootstrapProperties 是 獲取遠端(config-server)的 property sources

加載順序也可參考 http://{host}:{port}/actuator/env

PropertySources 單元測試順序:

  • @TestPropertySource#properties
  • @SpringBootTest#properties
  • @TestPropertySource#locations

三、參考資料

https://docs.spring.io/spring-boot/docs/2.0.5.RELEASE/reference/htmlsingle/#boot-features-external-config

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中HashMap和HashSet的高效使用技巧分享

    Java中HashMap和HashSet的高效使用技巧分享

    這篇文章主要為大家詳細介紹了Java中HashMap和HashSet的高效使用技巧,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-03-03
  • SpringBoot中controller深層詳細講解

    SpringBoot中controller深層詳細講解

    這篇文章主要介紹了SpringBoot在Controller層接收參數(shù)的常用方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • 淺談Java 對于繼承的初級理解

    淺談Java 對于繼承的初級理解

    下面小編就為大家?guī)硪黄獪\談Java 對于繼承的初級理解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • 解決spring @ControllerAdvice處理異常無法正確匹配自定義異常

    解決spring @ControllerAdvice處理異常無法正確匹配自定義異常

    這篇文章主要介紹了解決spring @ControllerAdvice處理異常無法正確匹配自定義異常的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java內(nèi)部排序之插入排序與交換排序詳解

    Java內(nèi)部排序之插入排序與交換排序詳解

    這篇文章主要介紹了Java內(nèi)部排序之插入排序與交換排序詳解,排序是將任意序列重新排列按照關(guān)鍵字有序,排序根基存儲器的不同分為內(nèi)部排序、外部排序,排序根據(jù)關(guān)鍵字分為穩(wěn)定排序、不穩(wěn)定排序,需要的朋友可以參考下
    2023-12-12
  • Java 實現(xiàn)倒計時功能(由秒計算天、小時、分鐘、秒)

    Java 實現(xiàn)倒計時功能(由秒計算天、小時、分鐘、秒)

    最近做項目遇到這樣的需求,天、小時、分鐘、秒的數(shù)值都是隔開的,服務(wù)器端只返回一個時間戳長度,怎么實現(xiàn)這樣的功能呢?下面小編給大家?guī)砹薐ava 實現(xiàn)倒計時功能的方案,需要的朋友參考下吧
    2018-01-01
  • java實現(xiàn)文件的上傳功能

    java實現(xiàn)文件的上傳功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)文件的上傳功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Springboot ApplicationRunner的使用解讀

    Springboot ApplicationRunner的使用解讀

    這篇文章主要介紹了Springboot ApplicationRunner的使用解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Spring-Validation 后端數(shù)據(jù)校驗的實現(xiàn)

    Spring-Validation 后端數(shù)據(jù)校驗的實現(xiàn)

    這篇文章主要介紹了Spring-Validation 后端數(shù)據(jù)校驗的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • Java?lambda表達式與泛型整理總結(jié)

    Java?lambda表達式與泛型整理總結(jié)

    Lambda?表達式(lambda?expression)是一個匿名函數(shù),Lambda表達式基于數(shù)學中的λ演算得名。泛型編程,故如其名,是一個泛化的編程方式。其實現(xiàn)原理為程序員編寫一個函數(shù)/類的代碼示例,讓編譯器去填補出不同的函數(shù)實現(xiàn)
    2022-07-07

最新評論