一.简介
在这篇文章中,我们将会了解到Spring是如何将配置文件中的属性值填充到bean对象中。对于填充属性这个过程,简单点来说,JavaBean 的每个属性通常都有 getter/setter 方法,我们可以直接调用 setter 方法将属性值设置进去。当然,这样做还是太简单了,填充属性的过程中还有许多事情要做。比如在 Spring 配置中,所有属性值都是以字符串的形式进行配置的,我们在将这些属性值赋值给对象的成员变量时,要根据变量类型进行相应的类型转换。对于一些集合类的配置,比如、和,还要将这些配置转换成相应的集合对象才能进行后续的操作。除此之外,如果用户配置了自动注入(autowire = byName/byType),Spring 还要去为自动注入的属性寻找合适的注入项。由此可以见,属性填充的整个过程还是很复杂的,并非是简单调用 setter 方法设置属性值即可。
接下来,我们深入到源码中,从源码中了解属性填充的整个过程。
二.源码分析
2.1 populateBean源码
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// 获取属性列表
PropertyValues pvs = mbd.getPropertyValues();
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
return;
}
}
boolean continueWithPropertyPopulation = true;
/*
* 在属性被填充前,给 InstantiationAwareBeanPostProcessor 类型的后置处理器一个修改
* bean 状态的机会。关于这段后置引用,官方的解释是:让用户可以自定义属性注入。比如用户实现一
* 个 InstantiationAwareBeanPostProcessor 类型的后置处理器,并通过
* postProcessAfterInstantiation 方法向 bean 的成员变量注入自定义的信息。当然,如果无
* 特殊需求,直接使用配置中的信息注入即可。另外,Spring 并不建议大家直接实现
* InstantiationAwareBeanPostProcessor 接口,如果想实现这种类型的后置处理器,更建议
* 通过继承 InstantiationAwareBeanPostProcessorAdapter 抽象类实现自定义后置处理器。
*/
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
/*
* 如果上面设置 continueWithPropertyPopulation = false,表明用户可能已经自己填充了
* bean 的属性,不需要 Spring 帮忙填充了。此时直接返回即可
*/
if (!continueWithPropertyPopulation) {
return;
}
// 根据名称或类型注入依赖
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 通过属性名称注入依赖
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 通过属性类型注入依赖
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
/*
* 这里又是一种后置处理,用于在 Spring 填充属性到 bean 对象前,对属性的值进行相应的处理,
* 比如可以修改某些属性的值。这时注入到 bean 中的值就不是配置文件中的内容了,
* 而是经过后置处理器修改后的内容
*/
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 对属性进行后置处理
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
// 应用属性值到 bean 对象中
applyPropertyValues(beanName, mbd, bw, pvs);
}
在populateBean函数中提供了这样的处理流程:
- 获取属性列表 pvs
- 在属性被填充到 bean 前,应用后置处理自定义属性填充。InstantiationAwareBeanPostProcessor处理器的postProcessAfterInstantiation函数的应用,此函数可以控制程序是否继续进行属性填充。
- 根据注入类型(byName/byType),提取依赖的bean,并统一存入PropertyValues中。
- 应用InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法,对属性获取完毕填充前对属性的再次处理。
- 将所有的PropertyValues中的属性填充至BeanWrapper中。
注意第3步,也就是根据名称或类型解析相关依赖(autowire)。该逻辑只会解析依赖,并不会将解析出的依赖立即注入到 bean 对象中。所有的属性值是在 applyPropertyValues 方法中统一被注入到 bean 对象中的。
下来对依赖注入(autowireByName/autowireByType)以及属性填充进一步分析实现细节。
2.2 autowireByName 方法分析
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
/*
* 获取非简单类型属性的名称,且该属性未被配置在配置文件中。这里从反面解释一下什么是"非简单类型"
* 属性,我们先来看看 Spring 认为的"简单类型"属性有哪些,如下:
* 1. CharSequence 接口的实现类,比如 String
* 2. Enum
* 3. Date
* 4. URI/URL
* 5. Number 的继承类,比如 Integer/Long
* 6. byte/short/int... 等基本类型
* 7. Locale
* 8. 以上所有类型的数组形式,比如 String[]、Date[]、int[] 等等
*
* 除了要求非简单类型的属性外,还要求属性未在配置文件中配置过,也就是 pvs.contains(pd.getName()) = false。
*/
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
// 检测是否存在与 propertyName 相关的 bean 或 BeanDefinition。若存在,则调用 BeanFactory.getBean 方法获取 bean 实例
if (containsBean(propertyName)) {
// 从容器中获取相应的 bean 实例
Object bean = getBean(propertyName);
// 将解析出的 bean 存入到属性值列表(pvs)中
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
这个方法的逻辑比较简单,该方法首先获取非简单类型属性的名称,然后再根据名称到容器中获得对应的bean实例,最后再将获取的bean添加到属性列表中。
2.3 autowireByType 方法分析
相比较autowireByName,autowireByType 则要复杂一些,复杂之处在于解析依赖的过程。我们复杂的地方当做一个黑盒,只需关注这个黑盒实现的功能即可。
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
// 获取非简单类型的属性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// 如果属性类型为 Object,则忽略,不做解析
if (Object.class != pd.getPropertyType()) {
/*
* 获取 setter 方法(write method)的参数信息,比如参数在参数列表中的
* 位置,参数类型,以及该参数所归属的方法等信息
*/
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
// 创建依赖描述对象
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
/*
* 下面的方法用于解析依赖。过程比较复杂,先把这里看成一个黑盒,我们只要知道这
* 个方法可以帮我们解析出合适的依赖即可。
*/
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
// 将解析出的 bean 存入到属性值列表(pvs)中
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
autowireByType首先也是获得非简单类型属性的名称,然后再根据属性名称获得属性描述符,并由属性描述符获取方法参数对象MethodParameter,随后在根据MethodParameter对象获取依赖描述符对象,整个过程为beanName->PropertyDescriptor->MethodParameter->DependencyDescriptor。在获取到依赖描述符对象后,再根据依赖描述符对象解析出合适的依赖,最后将解析出的结果存入属性列表pvs中即可。
2.4 解析依赖
2.4.1 resolveDependency
resolveDependency 依赖查找解决了以下场景:
- Optional:JDK8 提供了 API。主要是将依赖设置非强制依赖,即 descriptor.required=false。
- 延迟依赖注入支持:ObjectFactory、ObjectProvider、javax.inject.Provider 没有本质的区别。
- 另一种延迟注入的支持 - @Lazy 属性。
- 根据类型查找依赖 - doResolveDependency。
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (javaUtilOptionalClass == descriptor.getDependencyType()) {
return new OptionalDependencyFactory().createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
// 解析依赖
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
2.4.2 doResolveDependency
doResolveDependency 封装了依赖查找的各种情况:
- 快速查找: @Autowired 注解处理场景。AutowiredAnnotationBeanPostProcessor 处理 @Autowired 注解时,如果注入的对象只有一个,会将该 bean 对应的名称缓存起来,下次直接通过名称查找会快很多。
- 注入指定值:@Value 注解处理场景。QualifierAnnotationAutowireCandidateResolver 处理 @Value 注解时,会读取 @Value 对应的值进行注入。如果是 String 要经过三个过程:①占位符处理 -> ②EL 表达式解析 -> ③类型转换,这也是一般的处理过程,BeanDefinitionValueResolver 处理 String 对象也是这个过程。
- 集合依赖查询:直接全部委托给 resolveMultipleBeans 方法。
- 单个依赖查询:先调用 findAutowireCandidates 查找所有可用的依赖,如果有多个依赖,则根据规则匹配: @Primary -> @Priority -> ③方法名称或字段名称。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
// 该方法最终调用了 beanFactory.getBean(String, Class),从容器中获取依赖
Object shortcut = descriptor.resolveShortcut(this);
// 如果容器中存在所需依赖,这里进行断路操作,提前结束依赖解析逻辑
if (shortcut != null) {
return shortcut;
}
Class<?> type = descriptor.getDependencyType();
// 处理 @value 注解
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
// 解析数组、list、map 等类型的依赖
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
/*
* 按类型查找候选列表,如果某个类型已经被实例化,则返回相应的实例。
* 比如下面的配置:
*
* <bean name="mongoDao" class="xyz.coolblog.autowire.MongoDao" primary="true"/>
* <bean name="service" class="xyz.coolblog.autowire.Service" autowire="byType"/>
* <bean name="mysqlDao" class="xyz.coolblog.autowire.MySqlDao"/>
*
* MongoDao 和 MySqlDao 均实现自 Dao 接口,Service 对象(不是接口)中有一个 Dao
* 类型的属性。现在根据类型自动注入 Dao 的实现类。这里有两个候选 bean,一个是
* mongoDao,另一个是 mysqlDao,其中 mongoDao 在 service 之前实例化,
* mysqlDao 在 service 之后实例化。此时 findAutowireCandidates 方法会返回如下的结果:
*
* matchingBeans = [ <mongoDao, Object@MongoDao>, <mysqlDao, Class@MySqlDao> ]
*
* 注意 mysqlDao 还未实例化,所以返回的是 MySqlDao.class。
*
* findAutowireCandidates 这个方法逻辑比较复杂,我简单说一下它的工作流程吧,如下:
* 1. 从 BeanFactory 中获取某种类型 bean 的名称,比如上面的配置中
* mongoDao 和 mysqlDao 均实现了 Dao 接口,所以他们是同一种类型的 bean。
* 2. 遍历上一步得到的名称列表,并判断 bean 名称对应的 bean 是否是合适的候选项,
* 若合适则添加到候选列表中,并在最后返回候选列表
*
*/
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
// 抛出 NoSuchBeanDefinitionException 异常
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
/*
* matchingBeans.size() > 1,则表明存在多个可注入的候选项,这里判断使用哪一个
* 候选项。比如下面的配置:
*
* <bean name="mongoDao" class="xyz.coolblog.autowire.MongoDao" primary="true"/>
* <bean name="mysqlDao" class="xyz.coolblog.autowire.MySqlDao"/>
*
* mongoDao 的配置中存在 primary 属性,所以 mongoDao 会被选为最终的候选项。如
* 果两个 bean 配置都没有 primary 属性,则需要根据优先级选择候选项。优先级这一块
* 的逻辑没细看,不多说了。
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
// 抛出 NoUniqueBeanDefinitionException 异常
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
return null;
}
}
// 根据解析出的 autowiredBeanName,获取相应的候选项
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else { // 只有一个候选项,直接取出来即可
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// 返回候选项实例,如果实例是 Class 类型,则调用 beanFactory.getBean(String, Class) 获取相应的 bean。否则直接返回即可
return (instanceCandidate instanceof Class ?
descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
总结一下 doResolveDependency 的执行流程吧,如下:
- 首先将 beanName 和 requiredType 作为参数,并尝试从 BeanFactory 中获取与此对于的 bean。若获取成功,就可以提前结束 doResolveDependency 的逻辑。
- 处理 @value 注解
- 解析数组、List、Map 等类型的依赖,如果解析结果不为空,则返回结果
- 根据类型查找合适的候选项
- 如果候选项的数量为0,则抛出异常。为1,直接从候选列表中取出即可。若候选项数量 > 1,则在多个候选项中确定最优候选项,若无法确定则抛出异常
- 若候选项是 Class 类型,表明候选项还没实例化,此时通过 BeanFactory.getBean 方法对其进行实例化。若候选项是非 Class 类型,则表明已经完成了实例化,此时直接返回即可。
doResolveDependency 方法的四个功能,快速查找和集合处理都委托给了其它方法,注入指定值虽然看起来复杂,但占位符处理、EL 表达式解析、类型转换这三个功能点都有具体的类处理,也不是本文的重点。
重点看一下单个依赖的查询,弄明白了单个依赖的查询,其它集合依赖也差不多。
- 查找容器中所有可用依赖:findAutowireCandidates 方法根据类型查找依赖。
- 如何有多个依赖怎么处理?其实 Spring 有一套通用的流程,先按 @Primary 查找,再按 @Priority,最后按方法名称或字段名称查找,直到只有一个 bean 为止。相关的匹配规则见 determineAutowireCandidate 方法。
- 此时只有一个依赖,从容器获取真实的 bean。descriptor.resolveCandidate 方法根据名称 autowiredBeanName 实例化对象。
思考:findAutowireCandidates 返回的为什么是对象类型,而不是实例对象?
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
matchingBeans 中的 Object 对象可能是对象类型,而不全部是实例对象。因为 findAutowireCandidates 方法是根据类型 type 查找名称 beanNames,如果容器中该 beanName 还没有实例化,findAutowireCandidates 不会画蛇添足直接实例化该 bean,当然如果已经实例化了会直接返回这个 bean。
2.4.3 findAutowireCandidates
根据上面的分析,resolveDependency 方法对 Optional、延迟注入、懒加载注入等分别进行了处理。之后 doResolveDependency 在正式查找之前看能不能快速查找,如缓存 beanName、@Value 等快速指定需要注入的值,避免通过类型查找,最后才对集合依赖和单一依赖分别进行了处理。实际上,无论是集合依赖还是单一依赖查找,本质上都是调用 findAutowireCandidates 进行类型依赖查找。
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
// 1. Spring IoC 内部依赖 resolvableDependencies
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
// 2. 类型查找:本质上递归调用beanFactory#beanNamesForType。先匹配实例类型,再匹配bd。
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
for (String candidate : candidateNames) {
// 2.1 isSelfReference说明beanName和candidate本质是同一个对象
// isAutowireCandidate进一步匹配bd.autowireCandidate、泛型、@@Qualifier等进行过滤
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
// 2.2 添加到候选对象中
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 3. 补偿机制:如果依赖查找无法匹配,怎么办?包含泛型补偿和自身引用补偿两种。
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// 3.1 fallbackDescriptor: 泛型补偿,实际上是允许注入对象类型的泛型存在无法解析的情况
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
// 3.2 补偿1:不允许自称依赖,但如果是集合依赖,需要过滤非@Qualifier对象。什么场景?
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 3.3 补偿2:允许自称依赖,但如果是集合依赖,注入的集合依赖中需要过滤自己
if (result.isEmpty() && !multiple) {
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
findAutowireCandidates 大致可以分为三步:先查找内部依赖,再根据类型查找,最后没有可注入的依赖则进行补偿。
- 查找内部依赖:Spring IoC 容器本身相关依赖,这部分内容是用户而言是透明的,也不用感知。resolvableDependencies 集合中注册如 BeanFactory、ApplicationContext 、ResourceLoader、ApplicationEventPublisher 等。
- 根据类型查找:包括 ①外部托管 Bean ②注册 BeanDefinition。类型查找调用 beanFactory#beanNamesForType 方法。
- 自身引用:isSelfReference 方法判断 beanName 和 candidate 是否是同一个对象,包括两种情况:一是名称完全相同,二是 candidate 对应的工厂对象创建了 beanName。
- 是否可以注入:底层实际调用 resolver.isAutowireCandidate 方法进行过滤,包含三重规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。
- 补偿机制:如果依赖查找无法匹配,怎么办?Spring 提供了两种补偿机制:一是泛型补偿,允许注入对象对象的泛型无法解析,二是自身引用补偿,对这两种机制使用如下:
- 先使用泛型补偿,不允许自身引用:即 fallbackDescriptor。此时如果是集合依赖,对象必须是 @Qualifier 类型。
- 允许泛型补偿和自身引用补偿:但如果是集合依赖,必须过滤自己本身,即 beanName.equals(candidate) 必须剔除。
这个方法中调用的方法没有详细展开,想要了解的可以自己看看。
2.5 applyPropertyValues 方法分析
程序运行到这里,已经完成了对所有注入属性的获取,但是获取的属性是以PropertyValues形式存在的,还并没有应用到已经实例化的bean中,这一个工作是在applyPropertyValues方法中。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs == null || pvs.isEmpty()) {
return;
}
if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
}
MutablePropertyValues mpvs = null;
List<PropertyValue> original;
//检测属性列表是否已经转换过了,若转换了,直接填充属性
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 如果属性列表 pvs 被转换过,则直接返回即可
if (mpvs.isConverted()) {
try {
bw.setPropertyValues(mpvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
original = mpvs.getPropertyValueList();
}
else {
original = Arrays.asList(pvs.getPropertyValues());
}
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
//获取BeanDefinitionValueResolver,该Bean用于将bean定义对象中包含的值解析为应用于目标bean实例的实际值。
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// 用于存放实际解析后的属性集合
List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
boolean resolveNecessary = false;
// 遍历属性列表,解析原始值originalValue得到resolvedValue
for (PropertyValue pv : original) {
// 如果属性值被转换过,则就不需要再次转换
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
// 若该属性没有被解析过
String propertyName = pv.getName(); // 属性名称
Object originalValue = pv.getValue(); // 属性未经类型转换的值
/*
* 解析属性值。举例说明,先看下面的配置:
*
* <bean id="macbook" class="MacBookPro">
* <property name="manufacturer" value="Apple"/>
* <property name="width" value="280"/>
* <property name="cpu" ref="cpu"/>
* <property name="interface">
* <list>
* <value>USB</value>
* <value>HDMI</value>
* <value>Thunderbolt</value>
* </list>
* </property>
* </bean>
*
* 上面是一款电脑的配置信息,每个 property 配置经过下面的方法解析后,返回如下结果:
* propertyName = "manufacturer", resolvedValue = "Apple"
* propertyName = "width", resolvedValue = "280"
* propertyName = "cpu", resolvedValue = "CPU@1234" 注:resolvedValue 是一个对象
* propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"]
*
* 如上所示,resolveValueIfNecessary 会将 ref 解析为具体的对象,将 <list>
* 标签转换为 List 对象等。对于 int 类型的配置,这里并未做转换,所以
* width = "280",还是字符串。除了解析上面几种类型,该方法还会解析 <set/>、
* <map/>、<array/> 等集合配置
*/
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
/*
* convertible 表示属性值是否可转换,由两个条件合成而来。第一个条件不难理解,解释
* 一下第二个条件。第二个条件用于检测 propertyName 是否是 nested 或者 indexed,
* 直接举例说明吧:
*
* public class Room {
* private Door door = new Door();
* }
*
* room 对象里面包含了 door 对象,如果我们想向 door 对象中注入属性值,则可以这样配置:
*
* <bean id="room" class="xyz.coolblog.Room">
* <property name="door.width" value="123"/>
* </bean>
*
* isNestedOrIndexedProperty 会根据 propertyName 中是否包含 . 或 [ 返回
* true 和 false。包含则返回 true,否则返回 false。
* 关于 nested 类型的属性,大家还可以参考 Spring 的官方文档:
* https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conventions
*/
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
// 对于一般的属性,convertible 通常为 true
if (convertible) {
// 对属性值的类型进行转换,比如将 String 类型的属性值 "123" 转为 Integer 类型的 123
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
/*
* 如果 originalValue 是通过 autowireByType 或 autowireByName 解析而来,
* 那么此处条件成立,即 (resolvedValue == originalValue) = true
*/
if (resolvedValue == originalValue) {
if (convertible) {
// 可能将转换后的值存储在merged bean definition中,避免对每个创建的bean实例进行重新转换。
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
/*
* 如果原始值 originalValue 是 TypedStringValue,且转换后的值
* convertedValue 不是 Collection 或数组类型,则将转换后的值存入到 pv 中。
*/
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
try {
// 将所有的属性值设置到 bean 实例中
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
这个方法的执行流程如下:
- 检测属性值列表是否已转换过的,若转换过,则直接填充属性,无需再次转换
- 遍历属性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue
- 对解析后的属性值 resolvedValue 进行类型转换
- 将类型转换后的属性值设置到 PropertyValue 对象中,并将 PropertyValue 对象存入 deepCopy 集合中
- 将 deepCopy 中的属性信息注入到 bean 对象中
Spring IoC 依赖注入(三)resolveDependency
《Spring源码深度解析》