Spring中是如何为 @Value 注入的
Contents
本文讨论的 Spring 版本为 4.3.13-RELEASE
加载
PropertySourcesPlaceholderConfigurer
这个是当前Spring环境的所有 properties
的抽象.
可以看到它在 postProcessBeanFactory
时开始初始化整个 properties
输入源
每个输入源, 都保存在 org.springframework.core.env.MutablePropertySources
对象中, 该对象内部使用 private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();
来维护.
它的源码如下
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
this.propertySources.addLast(
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
);
}
try {
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
}
else {
this.propertySources.addLast(localPropertySource);
}
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
}
environmentProperties
- application.properties
- 在 application.properties 中通过
spring.profiles.include, spring.profiles.active
等方式的 - 或在代码中使用
@PropertySource(value = {"classpath:hello.properties"}), @Configuration
注解的, 都属于environmentProperties
environment
对象, 是由Spring框架基础设置进来的.
this.propertySources.addLast(
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
);
通过源码可以看到, 刚开始时, propertySources
是空的, 所以创建了一个 MutablePropertySources
对象. 然后将当前环境相关的 properties
添加到该对象中, 即:
localPropertySource
PropertySource<?> localPropertySource = new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
它会根据 localPropertySource
优先级(通过 localOverride
来判断, 默认是 false
) 来决定是添加到列表的开头还是结尾.
加载完成后, 就可以看到有如下的输入源了
触发 localPropertySource 的例子
package com.example.value.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class LocalPropertiesDemo {
@Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertyPlaceholderConfigurer.setLocation(new ClassPathResource("mylocal.properties"));
return propertyPlaceholderConfigurer;
}
}
优先级的例子
application.properties
local.value=from local application.properties
mylocal.properties
local.value=from local mylocal.properties
不设置 PropertySourcesPlaceholderConfigurer
的 setLocalOverride(true)
, 则结果:
curl http://localhost:8080/hello
from local application.properties
设置 setLocalOverride(true)
时
package com.example.value.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class LocalPropertiesDemo {
@Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertyPlaceholderConfigurer.setLocation(new ClassPathResource("mylocal.properties"));
propertyPlaceholderConfigurer.setLocalOverride(true);
return propertyPlaceholderConfigurer;
}
}
则结果:
curl http://localhost:8080/hello
from local mylocal.properties
总结
- 默认情况下, environmentProperties 优先级大于 localPropertySource
- 当设置了 setLocalOverride 为 true 时, 则 localPropertySource 优先级大于 environmentProperties
构建 PropertySourcesPropertyResolver
根据上面的加载完 propertySources
后, 就构建解析器, 最后进行相应的 Bean 处理了.
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
什么是 local properties
local properties 就是那些通过基于 PropertiesLoaderSupport
的API来手动或编程的方式配置的 properties . 例如 (setProperties, setLocations 等).
进行实际的注入
这就要回到 Spring 框架中 IoC 容器的处理了, 这里只是描述了一个简单的 @Value
是如何进行注入的, 其他的 @Autowired
等依赖注入是同理的.
Spring 框架中, 通过注解来进行注入的处理器, 是 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
通过名字, 也可以大概猜测它的意思了, 就是 Autowired
相关的注解处理器(默认情况下, 它也会处理 @Value
).
实际处理的代码源码(源文件的 599 行开始)
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
private final boolean required;
private volatile boolean cached = false;
private volatile Object cachedFieldValue;
public AutowiredFieldElement(Field field, boolean required) {
super(field, null);
this.required = required;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
}
}
可以大概看到 inject
方法的处理, 参数分别为(当前的 bean 对象, bean 的名字, 属性值)
然后通过 beanFactory
来将依赖的值解析出来 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
最后, 通过反射将该值”注”进去
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}