概述:
在上篇《 spring的启动过程04-bean后置处理器》文章中讲解了bean后置处理器的原理,这篇文章结合具体的处理器讲解spring@Value注解的处理过程。
spring容器会在两种场景用到properties文件的属性值,第一种替换XML文件中的占位符详情请查阅《spring的启动过程03.1-占位符替换过程-xml配置的参数》,第二种就是业务代码中采用@Value注解方式。
@Value(value = "${alias}")
private String alias;
@Value(value = "${password}")
private String password;
原理:
spring框架采用bean后置处理器来拦截bean属性值的注入,先看下AutowiredAnnotationBeanPostProcessor的静态类图。
核心方法触发点:
/**
* 创建Bean过程中设置属性值
* Populate the bean instance in the given BeanWrapper with the property values
* from the bean definition.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param bw BeanWrapper with bean instance
*/
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
PropertyValues pvs = ();
.....
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, );
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = (pvs, filteredPds, (), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
applyPropertyValues(beanName, mbd, bw, pvs);
}
遍历所有InstantiationAwareBeanPostProcessor实例设置属性字段值。
看下 属性替换的整个流程:
流程中用到了StringValueResolver类最终解析属性值,看下该类何时注入到bean工厂中
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = ();
// 替换占位符
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 (!(() && ())) {
BeanDefinition bd = (curName);
try {
(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException((), curName, (), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
// 把value注解处理器加入到bean工厂,该处理器解析value注解注入值
(valueResolver);
}
spring在执行bean工厂后置处理器的过程中会创建value标签解析器。负责创建该解析器的主要有
看下创建处理器的代码
1. PropertySourcesPlaceholderConfigurer
/**
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
doProcessProperties(beanFactoryToProcess, valueResolver);
}
2. PropertySourcesPlaceholderConfigurer
/**
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
();
();
();
StringValueResolver valueResolver = new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
String resolved = ignoreUnresolvablePlaceholders ?
(strVal) :
(strVal);
return ((nullValue) ? null : resolved);
}
};
doProcessProperties(beanFactoryToProcess, valueResolver);
}
看下PropertyPlaceholderConfigurer类处理器调用的核心代码
/**
* Resolve the given placeholder using the given properties, performing
* a system properties check according to the given mode.
* <p>The default implementation delegates to {@code resolvePlaceholder
* (placeholder, props)} before/after the system properties check.
* <p>Subclasses can override this for custom resolution strategies,
* including customized points for the system properties check.
* @param placeholder the placeholder to resolve
* @param props the merged properties of this configurer
* @param systemPropertiesMode the system properties mode,
* according to the constants in this class
* @return the resolved value, of null if none
* @see #setSystemPropertiesMode
* @see System#getProperty
* @see #resolvePlaceholder(String, )
*/
protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
String propVal = null;
// 默认采用locations配置的文件属性
if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
// 加载系统参数
propVal = resolveSystemProperty(placeholder);
}
if (propVal == null) {
// 从locations中加载参数
propVal = resolvePlaceholder(placeholder, props);
}
if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
// 如果从locations中没有发现参数,加载系统参数
propVal = resolveSystemProperty(placeholder);
}
return propVal;
}
/**
* Resolve the given key as JVM system property, and optionally also as
* system environment variable if no matching system property has been found.
* @param key the placeholder to resolve as system property key
* @return the system property value, or {@code null} if not found
* @see #setSearchSystemEnvironment
* @see System#getProperty(String)
* @see System#getenv(String)
*/
protected String resolveSystemProperty(String key) {
try {
String value = (key);
if (value == null && ) {
value = (key);
}
return value;
}
catch (Throwable ex) {
return null;
}
}
看下PropertySourcesPlaceholderConfigurer类处理器调用的核心代码
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if ( != null) {
for (PropertySource<?> propertySource : ) {
if (debugEnabled) {
(("Searching for key '%s' in [%s]", key, ()));
}
//获取参数值,参数值为env和配置的locations文件中的属性值
Object value = (key);
if (value != null) {
Class<?> valueType = ();
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
if (!(valueType, targetValueType)) {
}
return (value, targetValueType);
}
}
}
return null;
}
客户端使用引入方式:
第一种:
<context:property-placeholder location="classpath:" ignore-resource-not-found="true" ignore-unresolvable="true" />
该种方式默认的处理类为:
设置属性localOverride为true则location参数的优先级要大于系统变量。默认为false
第二种:
<bean class="">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:conf/*.properties</value>
</list>
</property>
<property name="fileEncoding" value="UTF-8" />
<property name="order" value="-128" />
</bean>
设置属性systemPropertiesMode为2标识系统参数优先级最高。默认为1
来看下上述两种方式参数的来源,从源码中可以看出优先级。
1. PropertyPlaceholderConfigurer
/**
* {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
* {@linkplain #processProperties process} properties against the given bean factory.
* @throws BeanInitializationException if any properties cannot be loaded
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
2. PropertySourcesPlaceholderConfigurer
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if ( == null) {
= new MutablePropertySources();
if ( != null) {
(
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, ) {
@Override
public String getProperty(String key) {
return (key);
}
}
);
}
try {
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
if () {
(localPropertySource);
}
else {
(localPropertySource);
}
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
processProperties(beanFactory, new PropertySourcesPropertyResolver());
= ;
}
总结:
spring通过bean工厂后置处理器处理beanDefinition对象中属性值占位符的替换功能并注册@value注解处理器到bean工厂中。在装饰bean属性的过程中会触发bean后置处理器处理@value注解并设置属性值,@value注解处理器从bean工厂中获得。