Источник: технология 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 ...
-
@PropertySource
annotations on your@Configuration
classes -
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