文章目录
最近学习SpringCloud感觉要疯了,硬着头皮写完了原理分析,有些原理理解的不是很透彻,还需要在琢磨琢磨。坚持吧!
1 @Value注解
通过上篇代码可知,我们只需要使用 @Value 注解,就能完成属性的注入。而 @Value 基于 Environment 的机制。我们先来看一下 @Value的注入过程,而 @Value 基于 Environment 的机制,具体请参考spring-cloud-config 源码解析,本文主要讲Environment初始化过程。
2 Spring Environment的初始化
在spring boot的启动流程中,有一个 prepareEnvironment 方法,这个方法就是用来准备Environment这个对象的,
- springApplication.run -> prepareEnvironment
这个方法主要就是创建和配置spring容器的环境信息,源码如下
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//根据上下文,创建一个合适的Environment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置Environment的propertySource、以及profile
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;
}
2.1 getOrCreateEnvironment
点击getOrCreateEnvironment() ,这个方法是根据当前的webApplication类型匹配对应的environment,当前默认的应该就是StandardServletEnvironment
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();
}
}
StandardServletEnvironment 是整个spring boot应用运行环境的实现类,后面所有的关于环境相关的配置操作都是基于这个类,它的类的结构图如下:
StandardServletEnvironment 的初始化过程
StandardServletEnvironment创建实例时会调用父类AbstractEnvironment无参构造器,AbstractEnvironment ,在这个类的构造方法中,会
调用一个自定义配置文件的方法customizePropertySources(). 这个方法被StandardServletEnvironment重写,所以最终调用StandardServletEnvironment 中的 customizePropertySources 方法
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);
}
这里是将几个不同的配置源封装成 StubPropertySource 和JndiPropertySource添加到
MutablePropertySources 中,调用 addLast 是表示一直往最后的位置添加。
SERVLET_CONFIG_PROPERTY_SOURCE_NAME:servlet的配置信息
SERVLET_CONTEXT_PROPERTY_SOURCE_NAME: 这个是servlet初始化的上下文,也就是以前我们在web.xml中配置的 context-param 。
JNDI_PROPERTY_SOURCE_NAME: 加载jndi.properties配置信息。
MutablePropertySources
在上面的代码中可以看到,所有的外部资源配置都是添加到了一个MutablePropertySources对象中,这个对象封装了属性资源的集合。
在AbstractEnvironment 这个类里定义了MutablePropertySources。并且把这个MutablePropertySources作为参数传递给了ConfigurablePropertyResolver 配置解析器中,而这个配置解析器是一个PropertySourcesPropertyResolver 实例。
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
}
来看一下这个类关系图
AbstractEnvironment 实现了文件解析器ConfigurablePropertyResolver ,而在上面这段代码中我们把 MutablePropertySources 传递到PropertySourcesPropertyResolver 中。这样就可以让 AbstractEnvironment 具备文件解析的功能,只是这个功能,委托给了PropertySourcesPropertyResolver来实现
2.2 configureEnvironment()
接下来再看SpringApplication.configureEnvironment()方法
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
//添加类型转化的服务
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//配置Environment中的propertysources
configurePropertySources(environment, args);
//配置profiles
configureProfiles(environment, args);
}
1 点击configurePropertySources()
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
//设置defaultProperties属性来源,如果设置了默认属性,则会加载
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
//设置commandLineProperties来源,如果设置了命令行参数,则会加载
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains</