上篇我们已经把SpringApplication.run过程中SpringApplicationRunListener的加载及starting流程讲解完了,本篇我们接着往下跟:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 本篇的主角,prepareEnvironment方法
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
...
}
}
具体看一下prepareEnvironment方法:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// 根据应用类型,创建应用环境:如得到系统环境变量、JVM及Servlet等参数
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 将 defaultProperties、commandLine及active-prifiles 属性加载到环境中
// commandLine 在 args 中配置
// 其它参数可在如下4个路径中配置:servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
// listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)
listeners.environmentPrepared((ConfigurableEnvironment)environment);
// 将环境绑定到SpringApplication
this.bindToSpringApplication((ConfigurableEnvironment)environment);
// 如果是非web环境,将环境转换成StandardEnvironment
if (this.webApplicationType == WebApplicationType.NONE) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertToStandardEnvironmentIfNecessary((ConfigurableEnvironment)environment);
}
// 配置PropertySources对它自己的递归依赖
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
下面我们一行一行来看。
getOrCreateEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
// 如果environment不为null,说明已初始化过,直接返回即可
if (this.environment != null) {
return this.environment;
} else {
// 若envirment为null,需要执行初始化
// 根据webApplicationType的类型来决定创建哪种environment
// 若是Web应用,则创建StandardServletEnvironment,否则创建StandardEnvironment
return (ConfigurableEnvironment)(this.webApplicationType == WebApplicationType.SERVLET ? new StandardServletEnvironment() : new StandardEnvironment());
}
}
我们以StandardServletEnvironment为例,看一下环境初始化过程中发生了什么。
public StandardServletEnvironment() {
}
额,好像就是个空构造函数,难道啥也没做?
哈哈哈,不要被迷惑了,我们知道,类执行构造函数时,会从顶层类往下依次执行,是不是StandardServletEnvironment的父类里有具体操作。
StandardServletEnvironment的父类为StandardEnvironment:
public StandardEnvironment() {
}
仍然为空构造,继续往上追踪父类,StandardEnvironment的父类为AbstractEnvironment:
哈哈哈,终于找到你了!!!
public AbstractEnvironment() {
this.propertySources = new MutablePropertySources(this.logger);
this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
// 关键是customizePropertySources方法
this.customizePropertySources(this.propertySources);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initialized " + this.getClass().getSimpleName() + " with PropertySources " + this.propertySources);
}
}
整个的继承关系如下:
AbstractEnvironment的customizePropertySources方法为protected修饰的空方法:
protected void customizePropertySources(MutablePropertySources propertySources) {
}
很明显,customizePropertySources的具体实现在子类StandardEnvironment及StandardServletEnvironment中。
先看StandardEnvironment的customizePropertySources方法:
protected void customizePropertySources(MutablePropertySources propertySources) {
// 获取系统参数
propertySources.addLast(new MapPropertySource("systemProperties", this.getSystemProperties()));
// 获取系统环境变量
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}
本质上底层还是走的System.getSystemProperties()和System.getenv()。
public Map<String, Object> getSystemProperties() {
try {
return System.getProperties();
} catch (AccessControlException var2) {
return new ReadOnlySystemAttributesMap() {
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
} catch (AccessControlException var3) {
if (AbstractEnvironment.this.logger.isInfoEnabled()) {
AbstractEnvironment.this.logger.info("Caught AccessControlException when accessing system property '" + attributeName + "'; its value will be returned [null]. Reason: " + var3.getMessage());
}
return null;
}
}
};
}
}
public Map<String, Object> getSystemEnvironment() {
if (this.suppressGetenvAccess()) {
return Collections.emptyMap();
} else {
try {
return System.getenv();
} catch (AccessControlException var2) {
return new ReadOnlySystemAttributesMap() {
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getenv(attributeName);
} catch (AccessControlException var3) {
if (AbstractEnvironment.this.logger.isInfoEnabled()) {
AbstractEnvironment.this.logger.info("Caught AccessControlException when accessing system environment variable '" + attributeName + "'; its value will be returned [null]. Reason: " + var3.getMessage());
}
return null;
}
}
};
}
}
}
接着看StandardServletEnvironment的customizePropertySources方法:
protected void customizePropertySources(MutablePropertySources propertySources) {
// 增加servletConfig初始化参数,当前没有任何值
propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
// 增加servletContext初始化参数,当前没有任何值
propertySources.addLast(new StubPropertySource("servletContextInitParams"));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource("jndiProperties"));
}
// 紧接着调用StandardEnvironment的customizePropertySources方法
super.customizePropertySources(propertySources);
}
说白了,StandardServletEnvironment比StandardEnvironment增加了2项Servlet相关的配置。
总结一句,getOrCreateEnvironment方法就是根据应用类型,创建应用环境:如得到系统环境变量、JVM及Servlet等参数。
configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
this.configurePropertySources(environment, args);
this.configureProfiles(environment, args);
}
先看,configurePropertySources方法:
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
// 从environment中获取PropertySources
MutablePropertySources sources = environment.getPropertySources();
// 如果defaultProperties不为null且不为空,将其加入sources
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 否则,根据args重新构建SimpleCommandLinePropertySource,然后更新或添加进sources
if (this.addCommandLineProperties && args.length > 0) {
String name = "commandLineArgs";
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
} else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
SimpleCommandLinePropertySource构造过程中,主要完成解析args字符串,然后提取出相关配置,具体源码可以自行看一下。
总结一下,configurePropertySources方法的目的是将args里的相关配置添加进environment中。
接着看configureProfiles方法:
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles();
Set<String> profiles = new LinkedHashSet(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
很明显,该方法的目的就是把active-prifiles属性加载到environment中。
environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
listener.environmentPrepared(environment);
}
}
本质上调用的是SpringApplicationRunListener的environmentPrepared方法。
看一下EventPublishingRunListener的environmentPrepared方法实现:
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
具体过程其实跟SpringApplicationRunListener的starting类似,也是先根据event类型筛选出符合条件的ApplicationListener继承类实例集合,然后分别调用各个ApplicationListener继承类实例的onApplicationEvent(event)方法完成事件的回调,具体过程可参见上篇--Springboot源码跟读2--SpringApplicationRunListener的加载及starting。
我们以spring.profiles.active配置的加载来看一下上述过程,在application.yml里配置:
spring:
profiles:
active: dev
Debug到上图断点处,看一下此时的environment:
可以看到,此时的activeProfiles仍旧为空,然后接着调用SpringApplicationRunListener的environmentPrepared,此时:
EventPublishingRunListener的environmentPrepared方法实现中,本质上是先将application、args、environment包装成ApplicationEnvironmentPreparedEvent,即表明环境已准备完毕的event。
上篇我们讲过,multicastEvent方法主要根据传入的event,从初始化后的ApplicationListeners中筛选出符合条件的ApplicationListener。
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
最后调用的是ApplicationListener的onApplicationEvent,而onApplicationEvent方法具体实现由ApplicationListener的各个继承类完成。
查看一下SpringApplication创建时初始化的所有ApplicationListener:
很明显,与配置加载相关的为ConfigFileApplicationListener。
查看一下ConfigFileApplicationListener的onApplicationEvent方法:
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
}
if (event instanceof ApplicationPreparedEvent) {
this.onApplicationPreparedEvent(event);
}
}
逻辑很简单,若传入的event是ApplicationEnvironmentPreparedEvent实例,则调用onApplicationEnvironmentPreparedEvent方法;若是ApplicationPreparedEvent实例,则调用onApplicationPreparedEvent方法。
跟一下onApplicationEnvironmentPreparedEvent方法:
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
postProcessors.add(this);
// 此处打一个断点
AnnotationAwareOrderComparator.sort(postProcessors);
Iterator var3 = postProcessors.iterator();
while(var3.hasNext()) {
EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
然后调用所有被加载的postProcessor的postProcessEnvironment方法来对environment进行后置处理,而ConfigFileApplicationListener的postProcessEnvironment方法主要扫描"classpath:/,classpath:/config/,file:./,file:./config/"下的配置文件然后将相关配置更新到environment中,具体代码不细讲,自行查看。
至此,我们将prepareEnvironment方法中的重要点走读完毕。