来源:[宜信技术学院 ] html
做者:石建伟java
在 SpringApplication#run(String... args)
方法中,外部化配置关键流程分为如下四步git
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); } ... }
对入口程序中标记的四步,分析以下github
SpringApplication#getRunListeners
加载 META-INF/spring.factories
获取 SpringApplicationRunListener
的实例集合,存放的对象是 EventPublishingRunListener
类型 以及自定义的 SpringApplicationRunListener
实现类型web
SpringApplication#prepareEnvironment
prepareEnvironment
方法中,主要的三步以下spring
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; }
2.一、getOrCreateEnvironment
方法bootstrap
在 WebApplicationType.SERVLET
web应用类型下,会建立 StandardServletEnvironment
,本文以 StandardServletEnvironment
为例,类的层次结构以下springboot
当建立 StandardServletEnvironment
,StandardServletEnvironment
父类 AbstractEnvironment
调用 customizePropertySources
方法,会执行 StandardServletEnvironment#customizePropertySources
和 StandardEnvironment#customizePropertySources
,源码以下app
AbstractEnvironment
dom
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 String SERVLET_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
顺序:
PropertySources
与PropertySource
关系为 1 对 N
2.二、configureEnvironment
方法
调用 configurePropertySources(environment, args)
, 在方法里面设置 Environment
的 PropertySources
, 包含 defaultProperties
和 SimpleCommandLinePropertySource
(commandLineArgs),PropertySources
添加 defaultProperties
到最后,添加 SimpleCommandLinePropertySource
(commandLineArgs)到最前面
PropertySources
顺序:
commandLineArgs
servletConfigInitParams
servletContextInitParams
jndiProperties
systemProperties
systemEnvironment
defaultProperties
2.三、listeners.environmentPrepared
方法
会按优先级顺序遍历执行 SpringApplicationRunListener#environmentPrepared
,好比 EventPublishingRunListener
和 自定义的 SpringApplicationRunListener
EventPublishingRunListener
发布 ApplicationEnvironmentPreparedEvent
事件
ConfigFileApplicationListener
监听 ApplicationEvent
事件 、处理 ApplicationEnvironmentPreparedEvent
事件,加载全部 EnvironmentPostProcessor
包括本身,而后按照顺序进行方法回调
ConfigFileApplicationListener#postProcessEnvironment
方法回调 ,而后addPropertySources
方法调用 RandomValuePropertySource#addToEnvironment
,在 systemEnvironment 后面添加 random,而后添加配置文件的属性源(详见源码 ConfigFileApplicationListener.Loader#load()
扩展点
自定义 SpringApplicationRunListener
,重写 environmentPrepared
方法
自定义 EnvironmentPostProcessor
自定义 ApplicationListener
监听 ApplicationEnvironmentPreparedEvent
事件
ConfigFileApplicationListener
,便是 EnvironmentPostProcessor
,又是 ApplicationListener
,类的层次结构以下
@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<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); // 加载本身 ConfigFileApplicationListener postProcessors.add(this); // 按照 Ordered 进行优先级排序 AnnotationAwareOrderComparator.sort(postProcessors); // 回调 EnvironmentPostProcessor for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } } List<EnvironmentPostProcessor> loadPostProcessors() { 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"); }
添加配置文件的属性源:
执行
new Loader(environment, resourceLoader).load();
, 调用load(Profile, DocumentFilterFactory, DocumentConsumer)
(getSearchLocations() 获取配置文件位置,能够指定经过 spring.config.additional-location 、spring.config.location 、spring.config.name 参数或者使用默认值 ), 而后调用addLoadedPropertySources -> addLoadedPropertySource
(加载 查找出来的PropertySource
到PropertySources
,并确保放置到 defaultProperties 的前面 )默认的查找位置,配置为
"classpath:/,classpath:/config/,file:./,file:./config/"
,查找顺序从后向前
PropertySources
顺序:
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 }
3.一、applyInitializers
方法
会遍历执行全部的 ApplicationContextInitializer#initialize
ApplicationContextInitializer
3.二、listeners.contextPrepared
方法
会按优先级顺序遍历执行 SpringApplicationRunListener#contextPrepared
,好比 EventPublishingRunListener
和 自定义的 SpringApplicationRunListener
SpringApplicationRunListener
,重写 contextPrepared
方法3.三、listeners.contextLoaded
方法
会按优先级顺序遍历执行 SpringApplicationRunListener#contextLoaded
,好比 EventPublishingRunListener
和 自定义的 SpringApplicationRunListener
EventPublishingRunListener
发布 ApplicationPreparedEvent
事件
ConfigFileApplicationListener
监听 ApplicationEvent
事件 处理 ApplicationPreparedEvent
事件
扩展点
自定义 SpringApplicationRunListener
,重写 contextLoaded
方法
自定义 ApplicationListener
,监听 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
// 回调处理(在配置类属性源解析) @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { reorderSources(this.context.getEnvironment()); } // 调整 PropertySources 顺序,先删除 defaultProperties, 再把 defaultProperties 添加到最后 private void reorderSources(ConfigurableEnvironment environment) { PropertySource<?> defaultProperties = environment.getPropertySources() .remove(DEFAULT_PROPERTIES); if (defaultProperties != null) { environment.getPropertySources().addLast(defaultProperties); } }
PropertySourceOrderingPostProcessor
是BeanFactoryPostProcessor
SpringApplication#refreshContext
会进行 @Configuration
配置类属性源解析,处理 @PropertySource
annotations on your @Configuration
classes,但顺序是在 defaultProperties 以后,下面会把 defaultProperties 调整到最后
AbstractApplicationContext#refresh
调用 invokeBeanFactoryPostProcessors
(PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
), 而后进行 BeanFactoryPostProcessor
的回调处理 ,好比 PropertySourceOrderingPostProcessor
的回调(源码见上文)
PropertySources
顺序:
commandLineArgs
servletConfigInitParams
servletContextInitParams
jndiProperties
systemProperties
systemEnvironment
random
application.properties …
@PropertySource
annotations on your @Configuration
classes
defaultProperties
不推荐使用这种方式,推荐使用在 refreshContext 以前准备好,
@PropertySource
加载太晚,不会对自动配置产生任何影响
EnvironmentPostProcessor
扩展public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor
ApplicationEnvironmentPreparedEvent
扩展public class ApplicationEnvironmentPreparedEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>
SpringApplicationRunListener
扩展public class CustomSpringApplicationRunListener implements SpringApplicationRunListener, Ordered
能够重写方法 environmentPrepared、contextPrepared、contextLoaded 进行扩展
ApplicationContextInitializer
扩展public class CustomApplicationContextInitializer implements ApplicationContextInitializer
关于与 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} 发送 GET 请求方式获取的
ApplicationPreparedEvent
扩展public class ApplicationPreparedEventListener implements ApplicationListener<ApplicationPreparedEvent>
在 classpath 下添加配置文件 META-INF/spring.factories
, 内容以下
# 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
以上的扩展能够选取其中一种进行扩展,只是属性源的加载时机不太同样
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