Источник: технология CreditEase.college.creditease.cn/Автор: Ши Цзяньвэй
1. Анализ процесса
процедура въезда
существуетSpringApplication#run(String... args)В методе ключевой процесс внешней конфигурации разделен на следующие четыре шага.
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,SpringApplication#getRunListeners
нагрузкаMETA-INF/spring.factoriesПолучатьSpringApplicationRunListenerКоллекция экземпляров хранимого объектаEventPublishingRunListenerтип и пользовательскийSpringApplicationRunListenerТип реализации
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;
}
2.1,getOrCreateEnvironmentметод
существуетWebApplicationType.SERVLETПод типом веб-приложения он создастStandardServletEnvironment, эта статья начинается сStandardServletEnvironmentНапример, иерархия классов выглядит следующим образом.
при созданииStandardServletEnvironment,StandardServletEnvironmentотецAbstractEnvironmentпередачаcustomizePropertySourcesметод, выполнит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 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заказ:
- servletConfigInitParams
- servletContextInitParams
- jndiProperties
- systemProperties
- systemEnvironment
PropertySourcesа такжеPropertySourceОтношение 1 к N
2.2,configureEnvironmentметод
передачаconfigurePropertySources(environment, args), заданный в методеEnvironmentизPropertySources, ВключатьdefaultPropertiesа такжеSimpleCommandLinePropertySource(командаLineArgs),PropertySourcesДобавить кdefaultPropertiesВ конце добавитьSimpleCommandLinePropertySource(commandLineArgs) на передний план
PropertySourcesзаказ:
-
commandLineArgs
-
servletConfigInitParams
-
servletContextInitParams
-
jndiProperties
-
systemProperties
-
systemEnvironment
-
defaultProperties
2.3,listeners.environmentPreparedметод
будет выполняться в приоритетном порядкеSpringApplicationRunListener#environmentPrepared,НапримерEventPublishingRunListenerи обычайSpringApplicationRunListener
-
EventPublishingRunListenerвыпускатьApplicationEnvironmentPreparedEventмероприятие-
ConfigFileApplicationListenerмониторApplicationEventсобытие, обработкаApplicationEnvironmentPreparedEventсобытие, загрузить всеEnvironmentPostProcessorВключите себя, затем сделайте обратные вызовы методов по порядку-
ConfigFileApplicationListener#postProcessEnvironmentобратный вызов метода, затемaddPropertySourcesвызов методаRandomValuePropertySource#addToEnvironment, добавьте random после systemEnvironment, а затем добавьте источник атрибута файла конфигурации (подробности см. в исходном коде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заказ:
- commandLineArgs
- servletConfigInitParams
- servletContextInitParams
- jndiProperties
- systemProperties
- systemEnvironment
- random
- application.properties ...
- defaultProperties
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
}
3.1,applyInitializersметод
будет перебирать всеApplicationContextInitializer#initialize
- точка расширения
- настроить
ApplicationContextInitializer
- настроить
3.2,listeners.contextPreparedметод
будет выполняться в приоритетном порядкеSpringApplicationRunListener#contextPrepared,НапримерEventPublishingRunListenerи обычайSpringApplicationRunListener
- точка расширения
- настроить
SpringApplicationRunListener, переписатьcontextPreparedметод
- настроить
3.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
4.SpringApplication#refreshContext
будет осуществляться@ConfigurationАнализ источника атрибута класса конфигурации, обработка@PropertySource annotations on your @Configurationклассы, но порядок после defaultProperties, следующее будет корректировать defaultProperties до конца
AbstractApplicationContext#refreshпередачаinvokeBeanFactoryPostProcessors (PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors), затем сделайтеBeanFactoryPostProcessorобработка обратного вызова, напримерPropertySourceOrderingPostProcessorобратный вызов (см. исходный код выше)
PropertySourcesзаказ:
-
commandLineArgs
-
servletConfigInitParams
-
servletContextInitParams
-
jndiProperties
-
systemProperties
-
systemEnvironment
-
random
-
application.properties ...
-
@PropertySourceannotations on your@Configurationclasses -
defaultProperties
Этот метод не рекомендуется, рекомендуется подготовиться перед обновлением контекста,
@PropertySourceЗагружено слишком поздно, чтобы повлиять на автоконфигурацию
2. Расширение внешних источников свойств конфигурации
1. На основеEnvironmentPostProcessorрасширять
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor
2. На основеApplicationEnvironmentPreparedEventрасширять
public class ApplicationEnvironmentPreparedEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>
3. На основеSpringApplicationRunListenerрасширять
public class CustomSpringApplicationRunListener implements SpringApplicationRunListener, Ordered
Может переопределять методы environmentPrepared, contextPrepared, contextLoaded для расширения.
4. На основеApplicationContextInitializerрасширять
public class CustomApplicationContextInitializer implements ApplicationContextInitializer
Что касается интеграции с клиентом Spring Cloud Config, расширение внешней загрузки конфигурации (привязанной к серверу конфигурации, инициализированной с помощью удаленных источников свойств)
Environment), обратитесь к исходному кодуPropertySourceBootstrapConfiguration(правдаApplicationContextInitializerрасширение),ConfigServicePropertySourceLocator#locateПолучить удаленные источники свойств
RestTemplateПолучено путем отправки запроса GET по адресу http://{spring.cloud.config.uri}/{spring.application.name}/{spring.cloud.config.profile}/{spring.cloud.config.label}.
5. На основеApplicationPreparedEventрасширять
public class ApplicationPreparedEventListener implements ApplicationListener<ApplicationPreparedEvent>
6. Расширьте реальный бой
6.1 Расширенная конфигурация
Добавить файл конфигурации в путь к классам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
Для расширения можно выбрать одно из вышеперечисленных расширений, но время загрузки источника атрибута отличается.
6.2 Расширенный пример кода
GitHub.com/сотня тысяч 823/tickets…
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 - получить источники свойств удаленного (конфигурационного сервера)
Порядок загрузки также может относиться к http://{host}:{port}/actuator/env.
PropertySourcesПорядок модульного тестирования:
@TestPropertySource#properties
@SpringBootTest#properties
@TestPropertySource#locations