Springboot源码跟读3--prepareEnvironment方法

上篇我们已经把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方法中的重要点走读完毕。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值