Analysis of spring boot externalized configuration

Time:2020-3-18

1、 Process analysis

1.1 entry procedure

In the spring application ා run (string… Args) method, the key process of externalizing configuration is 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);

			}

			...

	}

1.2 thinking map of key processes

1.3 key process details

For the four steps marked in the entry program, the analysis is as follows

1.3.1 SpringApplication#getRunListeners

Load meta-inf / spring.factories

Get springapplicationrunlistener

The objects stored in the instance collection of are eventpublishingrunnlistener type and customized springapplicationrunlistener implementation type

1.3.2 SpringApplication#prepareEnvironment

In the prepareenvironment method, the main three steps are as follows


			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 method

Under webapplicationtype.servlet web application type, standardservletenvironment will be created. In this paper, standardservletenvironment is taken as an example, and the class hierarchy is as follows

When the parent class abstractenvironment calls the customizepropertysources method when the standardservletenvironment is created, the standardservletenvironment customizepropertysources and standardenvironment customizepropertysources will be executed. The source code is 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

The relationship between propertysources and propertysource is 1 to n

2) Configureenvironment method

Call configurepropertysources (environment, args), set the environment’s propertysources in the method, including defaultproperties and

Simplecommandlinepropertysource (commandlineargs), propertysources add defaultproperties to the end, add

Simplecommandlinepropertysource (commandlineargs) to the front

Propertysources order:

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

3) Listeners.environmentprepared method

It will traverse and execute springapplicationrunlistener ා environmentprepared in priority order, such as eventpublishing runlistener and customized springapplicationrunlistener

Eventpublishing runlistener Publishing

Applicationenvironmentpreparedevent event

Configfileapplicationlistener listening

Applicationevent event, handle applicationenvironmentpreparedevent event, load all environmentpostprocessors including themselves, and then call back methods in order

—Configfileapplicationlistener? Postprocessenvironment method callback, then addpropertysources method call

RandomValuePropertySource#addToEnvironment, add random after the systemEnvironment, and then add the property source of the configuration file (see the source code ConfigFileApplicationListener.Loader#load ())

Extension point

  • Customize springapplicationrunlistener, override environmentprepared method
  • Custom environment postprocessor
  • Custom applicationlistener listens for applicationenvironmentpreparedevent events
  • Configfileapplicationlistener, that is, environmentpostprocessor and applicationlistener. The hierarchy of the class is as follows

@Override

			public void onApplicationEvent(ApplicationEvent event) {

			//Handling the applicationenvironmentpreparedevent event

			if (event instanceof ApplicationEnvironmentPreparedEvent) {

			onApplicationEnvironmentPreparedEvent(

			(ApplicationEnvironmentPreparedEvent) event);

			}

			//Handle the applicationpreparedevent event event

			if (event instanceof ApplicationPreparedEvent) {

			onApplicationPreparedEvent(event);

			}

			}

			private void onApplicationEnvironmentPreparedEvent(

			ApplicationEnvironmentPreparedEvent event) {

			//Load the environmentpostprocessor configured in meta-inf / spring.factories

			List

			//Load your own configfileapplicationlistener

			postProcessors.add(this);

			//Prioritize by ordered

			AnnotationAwareOrderComparator.sort(postProcessors);

			//Callback to 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);

			//Add property source for profile

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

	}

RandomValuePropertySource

public static void 

			addToEnvironment(ConfigurableEnvironment environment) {

			//Add random after system environment

			environment.getPropertySources().addAfter(

			StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,

			new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));

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

	}

Add property source for profile: Execute

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

Call load (profile, documentfilterfactory, documentconsumer) (getsearchlocations()

Gets the location of the configuration file, specifying the parameters through spring.config.additional-location, spring.config.location, spring.config.name or using the default value, and then calls addLoadedPropertySources > addLoadedPropertySource (loading the PropertySource to PropertySources, and ensuring that it is placed in front of defaultProperties).

Default find location, configured to

“Classpath: /, classpath: / config /, file:. /, file:. / config /”, the search order is from back to front

Propertysources order:

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

1.3.3 SpringApplication#prepareContext

In the preparecontext method, the main three steps are as follows


			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) Applyinitializer method

Will traverse and execute all applicationcontextinitializers

Extension point

Custom applicationcontextinitializer

2) Listeners.contextprepared method

It will traverse and execute springapplicationrunlistener ා contextprepared in priority order, such as eventpublishingrunnlistener and customized springapplicationrunlistener

Extension point

Customize springapplicationrunlistener, override contextprepared method

3) Listeners.contextloaded method

It will traverse and execute springapplicationrunlistener ා contextloaded in priority order, such as eventpublishingrunnlistener and customized springapplicationrunlistener

Eventpublishing runlistener Publishing

Applicationpreparedevent event event

Configfileapplicationlistener listening

Applicationevent event processing

Applicationpreparedevent event event

Extension point

  • Customize springapplicationrunlistener, override contextloaded method
  • Customize applicationlistener to listen for applicationpreparedevent events

ConfigFileApplicationListener

@Override

			public void onApplicationEvent(ApplicationEvent event) {

			//Handling the applicationenvironmentpreparedevent event

			if (event instanceof 

			ApplicationEnvironmentPreparedEvent) {

			onApplicationEnvironmentPreparedEvent(

			(ApplicationEnvironmentPreparedEvent) event);

			}

			//Handle the applicationpreparedevent event event

			if (event instanceof ApplicationPreparedEvent) {

			onApplicationPreparedEvent(event);

			}

			}

			private void onApplicationPreparedEvent(ApplicationEvent event) {

			this.logger.replayTo(ConfigFileApplicationListener.class);

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

			}

			//Add propertysourceorderingpostprocessor processor and configure propertysources

			protected void addPostProcessors(ConfigurableApplicationContext context) {

			context.addBeanFactoryPostProcessor(

			new PropertySourceOrderingPostProcessor(context));

	}

PropertySourceOrderingPostProcessor

//Callback processing (parsing in configuration class property source)

			@Override

			public void 

			postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

			throws BeansException {

			reorderSources(this.context.getEnvironment());

			}

			//Adjust the order of propertysources, delete defaultproperties first, and then add defaultproperties to the last

			private void reorderSources(ConfigurableEnvironment environment) {

			PropertySource

			.remove(DEFAULT_PROPERTIES);

			if (defaultProperties != null) {

			environment.getPropertySources().addLast(defaultProperties);

			}

	}

Propertysourceorderingpostprocessor is beanfactorypostprocessor

1.3.4 SpringApplication#refreshContext

The @ configuration class property source resolution will be performed, and @ propertysource annotations on your @ configuration classes will be processed, but the order is after defaultproperties, and the defaultproperties will be adjusted to the last

AbstractApplicationContext#refresh Call invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors),
Then it performs the callback processing of beanfactorypostprocessor, such as the callback of propertysourceorderingpostprocessor (see the source code above)

Propertysources order:

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

(this method is not recommended. It is recommended to prepare the refreshcontext before use, @ propertysource is loaded too late, and will not have any impact on automatic configuration.)

2、 Extended externalized configuration property source

2.1 environment based postprocessor extension


			public class CustomEnvironmentPostProcessor 

	implements EnvironmentPostProcessor

2.2 extension based on application environment preparedevent


			public class 

	ApplicationEnvironmentPreparedEventListener implements ApplicationListener

2.3 based on the springapplicationrunlistener extension


	public class CustomSpringApplicationRunListener implements SpringApplicationRunListener, Ordered

The methods environmentprepared, contextprepared and contextloaded can be overridden for extension

2.4 extension based on applicationcontextinitializer


	public class CustomApplicationContextInitializer implements ApplicationContextInitializer

For the integration with spring cloud config client and the extension of external configuration loading (binding to config server and initializing environment with remote property sources), refer to the source propertysourcebootstrap configuration (which is the extension of applicationcontextinitializer), configservicepropertysourcelocator ᦇ locate

Getting the remote property sources is obtained by resttemplate by sending a get request to http: / / {spring. Cloud. Config. URI} / {spring. Application. Name} / {spring. Cloud. Config. Profile} / {spring. Cloud. Config. Label}

2.5 based on the application preparedevent extension


			public class ApplicationPreparedEventListener 

	implements ApplicationListener

2.6 expand the actual combat

2.6.1 extended configuration

Add the configuration file meta-inf / spring.factories under 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

One of the above extensions can be selected for extension, but the loading time of attribute source is different

2.6.2 extension instance code

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]

Bootstrap properties is to get the property sources of the remote (config server)

The loading order can also refer to http: / / {host}: {port} / Actor / env

Propertysources unit test sequence:

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

3、 References

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

The above is the whole content of this article. I hope it will help you in your study, and I hope you can support developepaer more.