spring中使用的${} 以及@Value 注解的占位符,都在是spring容器初始化bean前,通过反射及类型转化把 占位符上的值转化为真实的值。
主要使用了 PropertySource、 PropertySources、PropertyResolver(PropertySourcesPropertyResolver)、PropertiesLoaderSupport(PropertySourcesPlaceholderConfigurer)类。
1、PropertySource
这个是org.springframework.core.env. PropertySource 抽象类,不是org.springframework.context.annotation.PropertySource注解哈。这个基类用来存key/value 键值对的,像Map、Properties类型的。
首先说下,这两个静态变量,因为定义为final 类型的 必须在构造方法中赋值。
这里的name 和 PropertSource 是有对应关系的,我们看下PropertySource 重写的equeals方法:
只要两个PropertSource 类的name值一样,那么就认为这是两个相同的PropertSource 对象。这就像我们使用@ PropertSource 注解在加载文件时,name就相当于被加载.prop文件的全路径名,如果两个文件名一样,那么就认为对应文件的内容是一样的。
在说这里的PropertSource<T> 抽象类,可以传入一个泛型的对象,这个对象用来处理这个抽象类的抽象方法:
/**
* Return the value associated with the given name,
* or {@code null} if not found.
* @param name the property to find
* @see PropertyResolver#getRequiredProperty(String)
*/
public abstract Object getProperty(String name);
像 MapPropertySource 类 传入 Map<String, Object> 对象,从而在getProperty方法里通过source.get(key) 的方式实现这个方法。
像PropertySourcesPlaceholderConfigurer类的postProcessBeanFactory方法中 通过传入Environment 类型的对象 ,通过Environment对象的source.getProperty(key)
方式实现这个方法。
2、PropertySources
package org.springframework.core.env;
/**
* Holder containing one or more {@link PropertySource} objects.
*
* @author Chris Beams
* @since 3.1
*/
public interface PropertySources extends Iterable<PropertySource<?>> {
/**
* Return whether a property source with the given name is contained.
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
boolean contains(String name);
/**
* Return the property source with the given name, {@code null} if not found.
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
PropertySource<?> get(String name);
}
这接口相当于PropertySource 的集合类,为PropertySource 服务的。
2.1 MutablePropertySources
这个类是对PropertyResources的子类,内部定义了一个 ArrayList 集合用于存放PropertySource 对象,
此外,这个类还有几个比较常用的方法:
其中 addFirst和addLast就是把对象放在List集合的最前面和最后面,很简单。
addBefore方法是把PropertySource对象放在指定的 对象前面,这里第一个参数是String name,根据这个name能找对集合中的目标PropertySource (PropertySource 中定义的name 属性,使得name -à PropertySource 一 一对应),进而找到这个PropertySource 对象在ArrayList中的index(实际是在数组的第index个),然后把给定的PropertySource对象 放在集合的index的位置(在数组的第index位),其他的往后移动一位。
/**
* Add the given property source object with precedence immediately higher
* than the named relative property source.
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName() +
"' with search precedence immediately higher than '" + relativePropertySourceName + "'");
}
//1、确保不能把自己放在自己前面
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
// 2、若原list中存在propertySource,就把它删了
removeIfPresent(propertySource);
//3、通过name找到需要放在PropertySource在数组中的索引位置
int index = assertPresentAndGetIndex(relativePropertySourceName);
//4、把给定的propertySource放在数组的index位,后面的全部后移一位
addAtIndex(index, propertySource);
}
addAfter和addBefore 原理一样。
/**
* Add the given property source object with precedence immediately lower
* than the named relative property source.
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName() +
"' with search precedence immediately lower than '" + relativePropertySourceName + "'");
}
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index + 1, propertySource);
}
3、PropertyResolver
这个接口是 Property解析器作用如下:
- 加载配置文件;
- 解析spring中的占位符 (如${}) ,进行类型转化,替换他们。
3.1 PropertySourcesPropertyResolver
看下这个类的构造方法:
所以在创建这个对象的时候必须传入 PropertySources ,这个接口是干啥的呢?存PropertySource<T>类型的集合啊, PropertySource<T>干啥的呢? 根据传入的泛型T,调用
public abstract Object getProperty(String name);
|
获取给定泛型中的key/value值。
继续看 PropertySourcesPropertyResolver 类的关键方法:
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
//1、在mutablePropertySources中的addfirst、addLast就是在这体现出来,放前面的先遍历呗
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
//2、调用PropertySource的 getProperty方法
Object value = propertySource.getProperty(key);
//3、去解决占位符以及类型转换的事去了
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Could not find key '" + key + "' in any property source");
}
return null;
}
4、PropertiesLoaderSupport
这个是抽象类,是加载Properties的基类,
PlaceholderConfigurerSupport
这个类是为了解决spring中的占位符的问题,它有两个具体的实现子类,一个是PropertyPlaceholderConfigurer ,一个是PropertySourcesPlaceholderConfigurer。
目前官方已经不建议使用PropertyPlaceholderConfigurer,保留它只是为了兼容spring3.1 以前的版本。官方推荐的是PropertySourcesPlaceholderConfigurer,不仅可以解决像${},还可以解决带有@Value的替换,除此之外,PropertySourcesPlaceholderConfigurer类还实现了EnvironmentAware接口从而获取Environment对象,通过该对象实现PropertySource类中的getProperty 方法。
public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer implements BeanNameAware, BeanFactoryAware |
4.1 PropertySourcesPlaceholderConfigurer
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware
PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是 BeanFactoryPostProcessor接口的一个实现。关于它的使用网上有很多的介绍,这里讲下原理,
PropertyResourceConfigurer 继承 BeanFactoryPostProcessor, PriorityOrdered
PlaceholderConfigurerSupport 继承 BeanNameAware, BeanFactoryAware
所以我们可以看到 PropertySourcesPlaceholderConfigurer 继承的有
BeanFactoryPostProcessor, PriorityOrdered, BeanNameAware, BeanFactoryAware, EnvironmentAware 五个接口。
最主要的方法是在实现BeanFactoryPostProcessor 接口的postProcessBeanFactory方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
this.propertySources.addLast(
//这里创建一个Propertysource< Environment >对象存在Propertysources中 new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
public String getProperty(String key) {
//这里的source就是environment对象 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);
}
}
//这里创建PropertySourcesPropertyResolver去解析上面加入的propertySources
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
}
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
propertyResolver.setValueSeparator(this.valueSeparator);
StringValueResolver valueResolver = new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
String resolved = (ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(nullValue) ? null : resolved);
}
};
doProcessProperties(beanFactoryToProcess, valueResolver);
}
doProcessProperties(beanFactoryToProcess, valueResolver);
这个方法是PlaceholderConfigurerSupport 抽象类里实现的
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
这样就把这个占位符的解析StringValueResolver 对象交给beanFactory,在实例化对象的时候通过java的反射给属性或方法赋值,在赋值前 需要进行类型转化,因为StringValueResolver得到的值永远是String类型的,这就涉及到spring的转化器了。
关于@Value注解的转化,最终是由QualifierAnnotationAutowireCandidateResolver 这个类去处理的。详情可参见下面的文章: