【Spring IOC】【五】容器源码解析- 属性填充populateBean
1 前言
好了,我们这篇文章讲解下populateBean,也就是bean的属性填充,并不仅仅是设置值,还有很多事情要做的。比如你的属性值类型转换、表达式解析等,关于属性填充的一些知识,本章先介绍这里。接下来,我们深入到源码中,从源码中了解属性填充的整个过程。
2 源码分析
2.1 populateBean方法通读
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // 基础校验 if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { return; } } /* InstantiationAwareBeanPostProcessor的后置处理 postProcessAfterInstantiation用于用户扩展 用户实现一个InstantiationAwareBeanPostProcessor 类型的后置处理器, 并通过postProcessAfterInstantiation 方法向 bean 的成员变量注入自定义的信息。 */ if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } // 获取到属性列表 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); // 根据名称或类型注入依赖 int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // 通过属性名称注入依赖 if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // 通过属性类型注入依赖 if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); // InstantiationAwareBeanPostProcessor的postProcessProperties后置处理 PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 对属性进行后置处理 PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } // 应用属性值到 bean 对象中 if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } }
那么大概方法的下执行流程。如下:
- 基础校验,获取属性列表 pvs
- 在属性被填充到 bean 前,应用后置处理自定义属性填充
- 根据名称或类型解析相关依赖(注意这里只是解析并获取属性对应的值,但不会将值直接赋值给属性)
- 再次应用后置处理,用于动态修改属性列表 pvs 的内容
- 将属性应用到 bean 对象中
注意第3步,也就是根据名称或类型解析相关依赖(autowire)。该逻辑只会解析依赖,并不会将解析出的依赖立即注入到 bean 对象中。也就是说所有的属性值是在 applyPropertyValues 方法中统一被注入到 bean 对象中的。其中涉及到InstantiationAwareBeanPostProcessor接口,有三个方法,会在该阶段进行调用。
有一个字段不知道你注意没有resolvedAutowireMode,这个值会决定执行或者不执行autowireByName和autowireByType,他是BeanDefinition中的一个信息,表示自动装配模式,在Spring中,注入方式有两种:
- 通过set方法
- 通过构造函数(如果有多个构造函数会选择参数多的构造方法)
手动装配注入:
-
@Resource: 默认是通过name来查找注入值,如果不存在就报错
-
@Autowired 通过类型查找(类型),然后再通过name
AutowireMode(自动装配模型)有四种模式分别是:
AUTOWIRE_NO = 0:默认装配模式,目前非xml配置都是使用这种方式,需要使用手动装配注入也就是我们常用的
AUTOWIRE_BY_NAME = 1: 通过set方法,并且set方法的名称需要和bean的name一致
AUTOWIRE_BY_TYPE = 2: 通过set方法,并且再根据bean的类型,注入属性,是通过类型配置
AUTOWIRE_CONSTRUCTOR = 3: 通过构造器注入
所以自己在调试的时候,注意下为什么你的断点走不进去autowireByName、autowireByType这方法中。
2.2 autowireByName
autowireByName方法来说,相对内容比较少,主要就是判断你的属性名字是否存在bean,存在的话会获取依赖bean,这里涉及到一个简单属性,也就是说Spring不是什么类型的都会给你做赋值,不在这简单属性范围内的,Spring才会给你处理,那么我们来看下源码:
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { // 筛选出非简单类型的属性 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { // 这里就是检测propertyName是否存在相关的bean或者beanDefinition,若存在即调用获取 if (containsBean(propertyName)) { Object bean = getBean(propertyName); // 存入pvs中 pvs.add(propertyName, bean); registerDependentBean(propertyName, beanName); if (logger.isTraceEnabled()) { logger.trace("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"); } } } }
2.3 autowireByType
autowireByType方法,顾名思义根据类型注入,这个方法相对来说会比较复杂,主要是在解析依赖的方法上,方法整体的源码如下:
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { // 获取bean工厂自定义的转换器 这个没懂是干啥的 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } Set<String> autowiredBeanNames = new LinkedHashSet<>(4); // 一样挑选出非简单的属性 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { // 属性描述 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); // Don't try autowiring by type for type Object: never makes sense, // even if it technically is an unsatisfied, non-simple property. if (Object.class != pd.getPropertyType()) { // 获取setter方法 位置,参数类型,以及该参数所归属的方法等信息 MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered); // 创建依赖描述对象 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); // 下面的方法用于解析依赖。过程比较复杂,解析依赖 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { registerDependentBean(autowiredBeanName, beanName); if (logger.isTraceEnabled()) { logger.trace("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); } } }
resolveDependency这个方法具体是怎么解析依赖的,我就暂时先不看了暂时用不到 我们平时装配策略都是走的默认模式,等空了,再细细研究下这个方法。
2.4 applyPropertyValues
乍一看这个方法内容还挺多,这个方法就会对属性进行赋值了,但是有的属性值不能直接赋值,还要做一层转换比如你的SPEL表达式,是不是还要解析,那么方法的大概内容如下:
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { if (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; // 如果被转换过了,就直接赋值返回了 if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. 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 valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. List<PropertyValue> deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; // 对属性进行逐一遍历 for (PropertyValue pv : original) { // 转换过直接跳过 if (pv.isConverted()) { deepCopy.add(pv); } else { String propertyName = pv.getName(); Object originalValue = pv.getValue(); if (originalValue == AutowiredPropertyMarker.INSTANCE) { Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod(); if (writeMethod == null) { throw new IllegalArgumentException("Autowire marker for property without write method: " + pv); } originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true); } Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { // 对属性值的类型进行转换,比如将 String 类型的属性值 "123" 转为 Integer 类型的 123 convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } // Possibly store converted value in merged bean definition, // in order to avoid re-conversion for every created bean instance. if (resolvedValue == originalValue) { if (convertible) { 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(); } // Set our (possibly massaged) deep copy. try { // 设置属性值 bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }
setPropertyValues,其实最后会调用setter方法进行设置:
最后来总结一下 applyPropertyValues 方法的执行流程吧,如下:
- 检测属性值列表是否已转换过的,若转换过,则直接填充属性,无需再次转换
- 遍历属性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue
- 对解析后的属性值 resolvedValue 进行类型转换
- 将类型转换后的属性值设置到 PropertyValue 对象中,并将 PropertyValue 对象存入 deepCopy 集合中
- 将 deepCopy 中的属性信息注入到 bean 对象中
3 问题引申
不知道你有没有跟我一样的困惑,在上一次的循环依赖中,A依赖B、B依赖A, 都是@Autowired注解方式依赖注入的,那么我们代码启动后,它既没有autowireByType也没有autowireByName,那它是怎么注入的呢?有没有?
其实注入的入口还是populateBean方法,我们上边不是说到他会有个后置处理器么,答案就在这里,有个AutowiredAnnotationBeanPostProcessor这个Spring提供的处理器帮我们做了这件事,他会解析有没有@Autowired或者@Resource注解,看图
进入到后置处理中,解析对象中的注解信息
解析到注解再中间会经过分析依赖,根据依赖描述信息最后回到getBean这个方法,有点意思。
那么@Resource又是怎么注入的呢?
有这个疑问么,其实和@Autowired时机是差不多的,也是用了一个后置处理器CommonAnnotationBeanPostProcessor:
4 小结
Spring真的神奇,帮我们做了很多事情,看人家的源码感觉很巧妙,每一个细节都很注重设计,不像我们平时写业务代码可能加一个场景对于我们来说就是再加个方法或者加个if分割一下逻辑,是不是有没有,多看多想,看看人家源码对于问题都是怎么去细致处理的,然后再运用到我们自身的业务场景中,有理解错的欢迎指正哈,谢谢。