【死磕-第三篇】prepareEnvironment
导读
- 上一篇讲述了,获取运行监听器,然后调用starting方法,干了些什么
- 这一篇主要讲springApplication.run这个流程中准备环境的这个过程
1. 源码详解
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* 翻译:运行一个spring程序,创建并且刷新一个新的ApplicationContext
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 创建一个计时器
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
// 创建一个可以配置的ConfigurableApplicationContext 变量
ConfigurableApplicationContext context = null;
// 创建一个异常报告集合
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置Headless属性 (这个就不详细讲了,大致意思是开启一个Headless模式,此模式会对没有显示器等输入输出设备的程序做一些处理)
configureHeadlessProperty();
// 获取运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用监听器的开始方法,内部会批量的调用监听器的starting方法,以发送事件等来间接调用ApplicationListener
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 1.1 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
- 进入正题, prepareEnvironment(listeners, applicationArguments);
// 1.2 准备环境
// 参数1: listeners 此内部有之前获得的eventPublishRunListener
// 参数2: applicationArguments 启动传入的参数
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
- 进入
prepareEnvironment
内部
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 获取或者创建一个环境(根据appliation的类型,SERVLET,REACTIVE,NONE)
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
- 进入
getOrCreateEnvironment()
内部
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
// 根据程序类型创建不同的环境
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
- 进入
new StandardServletEnvironment()
内部
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
// SERVLET_CONFIG_PROPERTY_SOURCE_NAME servletConfigInitParams
// SERVLET_CONTEXT_PROPERTY_SOURCE_NAME servletContextInitParams
// 向StandardServletEnvironment对象中添加二个propertySources,目前是空的
// Stub 存根,站位,差不多意思就是目前是空的,占个位置
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);
}
// 这是初始化servletProperties方法,此方法会在启动流程的刷新容器方法中被调用,会将上面二个propertySource 的值进行填充
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
}
- 跳转进入
StandardServletEnvironment
的父类,StandardEnvironment
内部
public class StandardEnvironment extends AbstractEnvironment {
/** 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 添加二个propertySource,一个是系统的属性,一个是系统的环境,直接就是有值的
// getSystemProperties() 会获得系统的属性值
// getSystemEnvironment() 会获得系统的环境值
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
- OK,至此就将ConfigurableEnvironment 新对象创建完了,接着就是配置里面的其他参数了
- 跳转进入
configureEnvironment
内部
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
// 采用单例模式获取一个ConversionService 对象
// ConversionService用于类型转化,我看了下加载完之后有136个转换器,例如String-》Number
// 在此之前EventPublishingRunListener中发送的starting() 事件触发了一个BackgroundPreinitializer
// 对此进行了预初始化
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
// 将类型转换对象设置到环境中去
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 配置属性资源
configurePropertySources(environment, args);
// 配置哪个配置文件生效
configureProfiles(environment, args);
}
- 跳转进入
configurePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
// 获取环境对象中的PropertySources,刚看了这个,不会忘了吧,应该是有四个对吧
// 系统属性 系统环境 servletConfigInitParams servletContextInitParams,前二个是有的,二个是空的
MutablePropertySources sources = environment.getPropertySources();
// 看看启动SpringApplicaiton的时候,有没有设置默认的属性以键值对的形式,有的话就加进去
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// addCommandLineProperties 默认是true
// 看看启动的时候有没有传入参数
if (this.addCommandLineProperties && args.length > 0) {
// COMMAND_LINE_PROPERTY_SOURCE_NAME commandLineArgs
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
// 看看资源中是否包含 commandLineArgs
if (sources.contains(name)) {
// 从资源获取 commandLineArgs
PropertySource<?> source = sources.get(name);
// 以commandLineArgs 为key 创建一个CompositePropertySource对象
CompositePropertySource composite = new CompositePropertySource(name);
// 向composite里加入 一个以springApplicationCommandLineArgs为key,传入的参数为值的PropertySource
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
// 并且将source 也加入到 composite中
composite.addPropertySource(source);
// 将sources中的 commandLineArgs 替换为 包含commandLineArgs 和 springApplicationCommandLineArgs的一个
// CompositePropertySource 对象
sources.replace(name, composite);
// 总结: 简单的说就是将属性中的命令行参数和spring启动的参数都放进composite中,然后将属性中原本的替换为现在的
// composite
}
else {
// 如果只有spring启动传入的参数的话,则以commandLineArgs为key,参数为值添加到最sources的最前面
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
- 刚这个过程这么复杂,其实要是不传参数啥的,以上代码都不会执行,所以不用管,主要就是将启动参数加入到属性资源里面来
- 至此属性资源中要是没有传参数的话,propertySource中还是有4个source,而且二个还是没有值
- 跳转进入
configureProfiles()
内部
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
// 从遍历PropertySources,从中获取属性为spring.profiles.active的属性
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
// 设置环境中生效的profiles,可以同时生效多个
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
-
到这里
configureEnvironment(environment, applicationArguments.getSourceArgs());
已经解析完毕了 -
接着回到
prepareEnvironment()
中,继续下一个流程
ConfigurationPropertySources.attach(environment);
- 进入
attach
方法内部
/**
* Attach a {@link ConfigurationPropertySource} support to the specified
* {@link Environment}.
*
* 翻译:给指定的环境添加一个ConfigurationPropertySource的支持
*
* Adapts each {@link PropertySource} managed by the environment
* to a {@link ConfigurationPropertySource} and allows classic
* {@link PropertySourcesPropertyResolver} calls to resolve using
* {@link ConfigurationPropertyName configuration property names}.
* <p>
*
* 翻译:将环境管理的每个PropertySource调整为ConfigurationPropertySource,
* 并允许经典的PropertySourcesPropertyResolver调用来解决使用
* ConfigurationPropertyName配置属性名称
*
* The attached resolver will dynamically track any additions or removals from the
* underlying {@link Environment} property sources.
*
* 翻译:所附的解析器将动态地跟踪底层环境属性源的任何添加或删除。
*
* @param environment the source environment (must be an instance of
* {@link ConfigurableEnvironment})
* @see #get(Environment)
*/
// 翻译:
// 给指定的环境添加一个ConfigurationPropertySource的支持
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
// 获取环境中的PropertySources
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
// ATTACHED_PROPERTY_SOURCE_NAME configurationProperties
// 获取sources中的configurationProperties
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
// 如果sources 中含有 configurationProperties,并且其中的source不是最新的,则移除他
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
// 在sources的最前面添加ConfigurationPropertySourcesPropertySource类型的configurationProperties,
// 内容就是环境中sources
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
- 目前按我的理解这个绑定就是给所有的sources换了个类型,这个类型提供一些方法可以更好的追踪,删除加入等操作
- 返回
prepareEnvironment()
流程 进入listeners.environmentPrepared(environment);
内部
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
- 此方法就是使用
EventPublishingRunListener
调用environmentPrepared
函数发送环境已经准备好的事件,从而触发监听器执行方法 - 我们来看看会触发哪些监听器
// 一共会触发 7 个监听器
0 = {ConfigFileApplicationListener@2420}
1 = {AnsiOutputApplicationListener@2421}
2 = {LoggingApplicationListener@2422}
3 = {ClasspathLoggingApplicationListener@2423}
4 = {BackgroundPreinitializer@2424}
5 = {DelegatingApplicationListener@2425}
6 = {FileEncodingApplicationListener@2426}
1.1 ConfigFileApplicationListener
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 加载环境后置处理器
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
// 并且加上自己,因为ConfigFileApplicationListener本身也是环境后置处理器
postProcessors.add(this);
// 排序
AnnotationAwareOrderComparator.sort(postProcessors);
// 分别调用他们的后置处理环境函数
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
- 进入
loadPostProcessors();
内部
List<EnvironmentPostProcessor> loadPostProcessors() {
// 从spring.factories中加载EnvironmentPostProcessor
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class.class, getClass().getClassLoader());
}
// 返回一共有4个,加上自己一共5个
// 0 = {SystemEnvironmentPropertySourceEnvironmentPostProcessor@2450}
// 1 = {SpringApplicationJsonEnvironmentPostProcessor@2451}
// 2 = {CloudFoundryVcapEnvironmentPostProcessor@2452}
// 3 = {ConfigFileApplicationListener@2286}
// 4 = {DebugAgentEnvironmentPostProcessor@2453}
- 好吧,一个一个来
- 进入
SystemEnvironmentPropertySourceEnvironmentPostProcessor
内部,看看是干嘛的
/**
* An {@link EnvironmentPostProcessor} that replaces the systemEnvironment
* {@link SystemEnvironmentPropertySource} with an
* {@link OriginAwareSystemEnvironmentPropertySource} that can track the
* {@link SystemEnvironmentOrigin} for every system environment property.
*
* 翻译:一个环境后置处理器,用于替换系统环境的属性资源,以便可以追踪,删除啥的,就是像之前的
* ConfigurationPropertySources.attach(environment) 的作用
* 将原本的 SystemEnvironmentPropertySource 替换成了 OriginAwareSystemEnvironmentPropertySource,值还是不变
* @author Madhura Bhave
* @since 2.0.0
*/
public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
/**
* The default order for the processor.
*/
public static final int DEFAULT_ORDER = SpringApplicationJsonEnvironmentPostProcessor.DEFAULT_ORDER - 1;
private int order = DEFAULT_ORDER;
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
// 获取“systemEnvironment”
PropertySource<?> propertySource = environment.getPropertySources().get(sourceName);
if (propertySource != null) {
// 将systemEnvironment的值换一个类进行封装
replacePropertySource(environment, sourceName, propertySource);
}
}
@SuppressWarnings("unchecked")
private void replacePropertySource(ConfigurableEnvironment environment, String sourceName,
PropertySource<?> propertySource) {
// 获取值
Map<String, Object> originalSource = (Map<String, Object>) propertySource.getSource();
// 将值重新封装
SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName,
originalSource);
// 将环境中原本的SystemEnvironmentPropertySource 换成 OriginAwareSystemEnvironmentPropertySource
environment.getPropertySources().replace(sourceName, source);
}
...
...
}
- 进入第2个
SpringApplicationJsonEnvironmentPostProcessor
内部
/**
* An {@link EnvironmentPostProcessor} that parses JSON from
* {@code spring.application.json} or equivalently {@code SPRING_APPLICATION_JSON} and
* adds it as a map property source to the {@link Environment}. The new properties are
* added with higher priority than the system properties.
*
* 翻译:
*
* @author Dave Syer
* @author Phillip Webb
* @author Madhura Bhave
* @author Artsiom Yudovin
* @since 1.3.0
*/
public class SpringApplicationJsonEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 获取所有的PropertySource
MutablePropertySources propertySources = environment.getPropertySources();
// 查找是否有spring.application.json,有的话解析此值,然后存储到环境中
propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst()
.ifPresent((v) -> processJson(environment, v));
}
private void processJson(ConfigurableEnvironment environment, JsonPropertyValue propertyValue) {
JsonParser parser = JsonParserFactory.getJsonParser();
// 获取spring.application.json 的值,转换成map
Map<String, Object> map = parser.parseMap(propertyValue.getJson());
if (!map.isEmpty()) {
// 添加到环境中去
addJsonPropertySource(environment, new JsonPropertySource(propertyValue, flatten(map)));
}
}
...
...
}
- 简单的使用方法是,添加一个spring.application.json的系统属性,然后值是json形式的,就可以向propertysource中加入属性了
- 进入第3个
CloudFoundryVcapEnvironmentPostProcessor
内部
不知道这个是什么东西
- 进入第4个
ConfigFileApplicationListener
内部
// 提取ConfigFileApplicationListener中后置处理器部分
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
...
...
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 添加一个RandomValuePropertySource到propertysource中
RandomValuePropertySource.addToEnvironment(environment);
// 创建一个Loader,并执行load()方法
new Loader(environment, resourceLoader).load();
}
...
...
}
- 进入
new Loader()
内部(Loader
是ConfigFileApplicationListener
的内部类)
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
// 创建一个占位符解析器,内部是 PropertyPlaceholderHelper,默认占位符 ${}
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader
: new DefaultResourceLoader(getClass().getClassLoader());
// 从spring.factories中加载 PropertySourceLoader, 加载了以下二个
// 0 = {PropertiesPropertySourceLoader@2621} 用于加载后缀为properties的资源
// 1 = {YamlPropertySourceLoader@2622} 用于加载后缀为yaml的资源
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
- 创建好了加载器,那就使用它来加载资源吧
- 进入
load()
内部
void load() {
// 根据属性等筛选加载资源
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 初始化配置
initializeProfiles();
while (!this.profiles.isEmpty()) {
// 取出第一个
Profile profile = this.profiles.poll();
// 判断是否是默认的配置
if (isDefaultProfile(profile)) {
// 添加配置到环境中
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
- 进入
FilteredPropertySource.apply()
内部
static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties,
Consumer<PropertySource<?>> operation) {
// 获取所有的propetiesource,目前应该是只有6个,最近刚添加了个RandomValuePropertySource
MutablePropertySources propertySources = environment.getPropertySources();
// 获取资源中名为defaultProperties的资源
PropertySource<?> original = propertySources.get(propertySourceName);
if (original == null) {
// 没有的话,则直接调用传入的匿名方法
operation.accept(null);
return;
}
// 如果有的话,则将资源替换为FilteredPropertySource,添加2个额外的属性,spring.profiles.active,spring.profiles.include用于筛选
propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
try {
operation.accept(original);
}
finally {
propertySources.replace(propertySourceName, original);
}
}
- 总结下来就是根据传入的参数,以及生效的profile 来加载配置文件
- 最后一个就是DebugAgentEnvironmentPostProcessor, 进入DebugAgentEnvironmentPostProcessor内部
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (ClassUtils.isPresent(REACTOR_DEBUGAGENT_CLASS, null)) {
Boolean agentEnabled = environment.getProperty(DEBUGAGENT_ENABLED_CONFIG_KEY, Boolean.class);
if (agentEnabled != Boolean.FALSE) {
try {
Class<?> debugAgent = Class.forName(REACTOR_DEBUGAGENT_CLASS);
debugAgent.getMethod("init").invoke(null);
}
catch (Exception ex) {
throw new RuntimeException("Failed to init Reactor's debug agent");
}
}
}
}
- 用于快速加载此类
1.2 AnsiOutputApplicationListener
- 用于配置ansi编码
1.3 LoggingApplicationListener
- 根据配置的属性设置配置日志
1.4 ClasspathLoggingApplicationListener
- 根据日志的输出等级打印classpath的信息
1.5 BackgroundPreinitializer
- 在此事件中不会执行操作
1.6 DelegatingApplicationListener
- 在此事件中不会执行操作
1.7 FileEncodingApplicationListener
- 根据配置来判断文件的类型是否符合配置