First, process analysis
1.1 entry program
In SpringApplication # run (String ... args) method, the external configuration of the key processes divided into the following four steps
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); } ... }
FIG critical processes mind 1.2
1.3 Detailed critical processes
Four-step procedure for the entry mark, as follows
1.3.1 Spring application # getRunListeners
Load META-INF / spring.factories
Get SpringApplicationRunListener
Set of examples, the object is stored in EventPublishingRunListener type of custom implementation types SpringApplicationRunListener
1.3.2 SpringApplication#prepareEnvironment
prepareEnvironment method, the following three steps main
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 方法
In WebApplicationType.SERVLET web application type, will create StandardServletEnvironment, paper StandardServletEnvironment for example, the following class hierarchy
When creating StandardServletEnvironment, StandardServletEnvironment parent AbstractEnvironment call customizePropertySources method performs StandardServletEnvironment # customizePropertySources and StandardEnvironment # customizePropertySources, source code as follows 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 order:
-
servletConfigInitParams
-
servletContextInitParams
-
jndiProperties
-
systemProperties
-
systemEnvironment
PropertySources PropertySource relationship with one pair of N
2) configureEnvironment method
Call configurePropertySources (environment, args), which is provided in a method PropertySources Environment, and comprising defaultProperties
SimpleCommandLinePropertySource (commandLineArgs), PropertySources to add defaultProperties Finally, add
SimpleCommandLinePropertySource (commandLineArgs) to the front
PropertySources order:
-
commandLineArgs
-
servletConfigInitParams
-
servletContextInitParams
-
jndiProperties
-
systemProperties
-
systemEnvironment
-
defaultProperties
3) listeners.environmentPrepared 方法
It will be traversed in order of priority the implementation of SpringApplicationRunListener # environmentPrepared, such as EventPublishingRunListener and custom SpringApplicationRunListener
EventPublishingRunListener release
ApplicationEnvironmentPreparedEvent 事件
- ConfigFileApplicationListener monitor
ApplicationEvent event, ApplicationEnvironmentPreparedEvent event handling, loading all EnvironmentPostProcessor including their own, and then follow the order of the callback method
--- ConfigFileApplicationListener # postProcessEnvironment callback method, then the method call addPropertySources
RandomValuePropertySource # addToEnvironment, was added after the random systemEnvironment, then add attributes of the source profile (see source ConfigFileApplicationListener.Loader # load ()
Extension point
-
Custom SpringApplicationRunListener, rewrite method environmentPrepared
-
Custom EnvironmentPostProcessor
-
Custom ApplicationListener listening ApplicationEnvironmentPreparedEvent event
-
ConfigFileApplicationListener, that is EnvironmentPostProcessor, is ApplicationListener, class hierarchy as follows
@Override public void onApplicationEvent(ApplicationEvent event) { // 处理 ApplicationEnvironmentPreparedEvent 事件 if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } // 处理 ApplicationPreparedEvent 事件 if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } } private voidonApplicationEnvironmentPreparedEvent ( ApplicationEnvironmentPreparedEvent Event ) { // load EnvironmentPostProcessor META-INF / spring.factories configured List // load its ConfigFileApplicationListener postProcessors.add ( the this ); // be prioritized according to the Ordered AnnotationAwareOrderComparator.sort (postProcessors); // callback 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"); }
Add a profile properties Source: execution
new Loader(environment, resourceLoader).load();,
调用 load(Profile, DocumentFilterFactory, DocumentConsumer)(getSearchLocations()
Get the configuration file location can be specified by spring.config.additional-location, spring.config.location, spring.config.name parameters or use the default value), then call addLoadedPropertySources -> addLoadedPropertySource (loaded Find out PropertySource to PropertySources, and placed in the front ensuring defaultProperties)
Find the default location, configure
"Classpath: /, classpath: / config /, file: ./, file: ./ config /", find the order from back to front
PropertySources order:
-
commandLineArgs
-
servletConfigInitParams
-
servletContextInitParams
-
jndiProperties
-
systemProperties
-
systemEnvironment
-
random
-
application.properties ...
-
defaultProperties
1.3.3 SpringApplication#prepareContext
prepareContext method, the following three steps main
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 method
It will traverse perform all ApplicationContextInitializer # initialize
Extension point
- Custom ApplicationContextInitializer
2)listeners.contextPrepared 方法
It will be traversed in order of priority the implementation of SpringApplicationRunListener # contextPrepared, such as EventPublishingRunListener and custom SpringApplicationRunListener
Extension point
Custom SpringApplicationRunListener, rewrite method contextPrepared
3)listeners.contextLoaded 方法
It will be traversed in order of priority the implementation of SpringApplicationRunListener # contextLoaded, such as EventPublishingRunListener and custom SpringApplicationRunListener
EventPublishingRunListener release
ApplicationPreparedEvent 事件
- ConfigFileApplicationListener monitor
ApplicationEvent event processing
ApplicationPreparedEvent 事件
Extension point
-
Custom SpringApplicationRunListener, rewrite method contextLoaded
-
Custom ApplicationListener, listening ApplicationPreparedEvent event
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
// callback handler (source attribute in the configuration class parsing) @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { reorderSources ( the this .context.getEnvironment ()); } // adjust PropertySources order, delete defaultProperties, then add defaultProperties Finally, the Private void reorderSources (ConfigurableEnvironment Environment) { PropertySource .remove (DEFAULT_PROPERTIES); IF (! defaultProperties = null ) { . environment.getPropertySources () addLast (defaultProperties); } }
PropertySourceOrderingPostProcessor 是 BeanFactoryPostProcessor
1.3.4 SpringApplication#refreshContext
@Configuration configuration will be parsed source class attribute, processing @PropertySource annotations on your @Configuration classes, but the order is after defaultProperties, the following will be adjusted to the final defaultProperties
AbstractApplicationContext # refresh call invokeBeanFactoryPostProcessors (PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors), then the callback process BeanFactoryPostProcessor, such PropertySourceOrderingPostProcessor callback (Source see above)
PropertySources order:
-
commandLineArgs
-
servletConfigInitParams
-
servletContextInitParams
-
jndiProperties
-
systemProperties
-
systemEnvironment
-
random
-
application.properties ...
-
@PropertySource annotations on your @Configuration classes
-
defaultProperties
(Not recommended for use in this way, it is recommended to use ready before refreshContext, @ PropertySource load too late, will not have any impact on the auto-configuration)
Second, to expand the external source of configuration properties
2.1 Based on EnvironmentPostProcessor extension
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor
2.2 Based on ApplicationEnvironmentPreparedEvent extension
public class ApplicationEnvironmentPreparedEventListener implements ApplicationListener
2.3 Based on SpringApplicationRunListener extension
public class CustomSpringApplicationRunListener implements SpringApplicationRunListener, Ordered
The method can override environmentPrepared, contextPrepared, contextLoaded extended
2.4 Based on ApplicationContextInitializer extension
public class CustomApplicationContextInitializer implements ApplicationContextInitializer
About integration with Spring Cloud Config Client, the external configuration of the extended load (bound to the Config Server, use the remote property sources initialization Environment), a reference source PropertySourceBootstrapConfiguration (ApplicationContextInitializer is an extension of), ConfigServicePropertySourceLocator # locate
Get property sources is distal to RestTemplate through http: // {spring.cloud.config.uri} / {spring.application.name} / {spring.cloud.config.profile} / {spring.cloud.config.label } acquired transmission request method GET
2.5 Based on ApplicationPreparedEvent extension
public class ApplicationPreparedEventListener implements ApplicationListener
2.6 Extended combat
2.6.1 Extended configuration
Add a profile META-INF / spring.factories at classpath, as follows
# 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
You can select more than one extension extended, but the timing of loading properties of the source are not the same
Examples of spreading codes 2.6.2
https://github.com/shijw823/springboot-externalized-configuration-extend.git
PropertySources order:
-
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 is to obtain a distal end (config-server) of the property sources
Loading sequence may also refer to http: // {host}: {port} / actuator / env
PropertySources unit testing sequence:
-
@ # TestPropertySource properties
-
@SpringBootTest#properties
-
@TestPropertySource#locations
Third, reference materials
https://docs.spring.io/spring-boot/docs/2.0.5.RELEASE/reference/htmlsingle/#boot-features-external-config
Author: Shi Jianwei