Spring读源码系列番外篇09--BeanWrapper的应用

x33g5p2x  于2022-07-19 转载在 Spring  
字(11.1k)|赞(0)|评价(0)|浏览(444)

本文主要讲解BeanWrapper在getBean流程中populateBean方法中的应用。

populateBean

populateBean方法在Spring的getBean流程可谓是声名远扬,大部分看多源码或者没看过源码的人都或多或少听到过这个底层属性注入方法,而今天要做的就是在学完BeanWrapper后,来彻底剖析一下这个过程。

  • populateBean方法有三个参数: beanName,bean定义信息,和对createBeanInstance创建出来但还没赋值的bean进行包装的beanWrapper
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)

如果不看方法的源码,其实也能猜到这个方法的大概逻辑,就是取出mdb中保存的PropertyValues,然后通过bw将这些PropertyValues设置内部被包装的bean即可。

大体逻辑与上述讲的类似,但是还是有些区别,具体的流程如下:

  • 前置校验,有没有属性需要注入
//如果beanWrapper为空的情况下,还有属性需要注入,那就抛出异常---这里是逻辑乱套的问题
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

注意populateBean方法不只在getBean流程中被调用,该方法也会在其他地方被调用,所以这个前置判断自有其存在的道理

  • 判断当前bean是否需要跳过属性注入的步骤
//isSynthetic判断当前bean是否是应用程序内部合成的bean,如果是,那么跳过后置处理步骤
       //hasInstantiationAwareBeanPostProcessors是否存在相关后置处理器
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
			//调用每个后置处理器的postProcessAfterInstantiation---实例化后进行后置处理
			//返回值决定是否要跳过属性注入的步骤
				if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					return;
				}
			}
		}
  • 对不同注入类型进行判断
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
        
        //获取当前bean的注入模式---是按照类型注入,还是按照name注入   
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// 处理按照name注入
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			//处理按照类型注入
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}
  • 按照name注入
protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        //从mbd中取出PropertyValues,然后排除掉不符合条件的PropertyValue
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		//遍历符合条件的PropertyValues
		for (String propertyName : propertyNames) {
		//容器中是否存在该bean
			if (containsBean(propertyName)) {
			//拿到propertyName在容器中对应的bean,然后加入pvs中
				Object bean = getBean(propertyName);
				pvs.add(propertyName, bean);
				//注册依赖的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");
				}
			}
		}
	}
  • unsatisfiedNonSimpleProperties的排除规则是什么 ?
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
		//有序的set集合
		Set<String> result = new TreeSet<>();
		PropertyValues pvs = mbd.getPropertyValues();
		PropertyDescriptor[] pds = bw.getPropertyDescriptors();
		for (PropertyDescriptor pd : pds) {
		//首先当前属性在目标对象中有对应的setter方法
		//并且不是被排除的依赖注入: 例如相关aware接口的实现 ---当然还有一些情况,也需要忽略,这里大家自行去看即可
			if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
			//不是一些简单类型,例如: Integer,Long等值类型
					!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
			//将满足条件的加入集合中		
				result.add(pd.getName());
			}
		}
		return StringUtils.toStringArray(result);
	}
  • 在prepareBeanFactory方法中,会设置需要忽略哪些依赖注入

  • 按照类型注入
protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        //用户是可以自行设置TypeConverter的
		TypeConverter converter = getCustomTypeConverter();
		//如果没有设置,就用传入的BeanWrapper,因为BeanWrapper实现了TypeConverter接口,不清楚的回看之前beanWrapper的讲解
		if (converter == null) {
			converter = bw;
		}
        
		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
		//排除掉mbd中不符合规则的属性
		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 a unsatisfied, non-simple property.
				//如果当前属性的类型是Object,那么也不会进入注入
				if (Object.class != pd.getPropertyType()) {
				   //拿到当前属性对应的setter方法
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
				    	
					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
					//拿到依赖描述符
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
					//按照类型对依赖进行解析---然后返回按照类型解析后,得到的bean---这里不作为讲解重点
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);  
					//如果得到的bean不为空,那么说明当前propertyName需要设置的值就是上面得到的bean,即autowiredArgument 
					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方法的实现感兴趣可以自行研究,这里主要还是讲后面BeanWrapper发挥的作用

  • 无论是按照类型注入还是按照name注入,这一步都是先将非值类型的属性,需要注入的bean都拿到,然后进行替换

在没有按照name和type进行依赖注入前,bean定义中保存的属性既有值类型的,例如: name=大忽悠.age=18
姓名一般都是字符串,因此配置文件中设置好了,是不需要进行类型转换的,但是age年龄,一般都是整型,因此需要将配置文件中设置的18,由字符串转换为整型才行。

也有非值类型,例如: dog,cat,pig , 这些都是用户自定义的对象,因此要设置这些属性的值,就需要告诉Spring是按照beanName去容器中定位bean,然后将对应的bean设置为属性值。

还是按照类型去IOC容器中进行匹配,然后进行设置。

因此在经过autowireByName和autowireByType之后,是将当前beanDefinition中非值类型的propertyValue的value进行了更新,更新了为在容器中找到的bean对象、
注意,此时相关的值还保存在beanDefinition的propertyValues中,并没有设置到beanWrapper包装的目标对象中

  • 我们知道可以用一些@Autowired等注解完成属性注入,并且这些注解底层靠的是bean后置处理器完成的,而对应后置处理器关于属性注入方法的调用时机,就出现在populateBean进行完依赖注入模式分析之后
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		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);
		}

这段关于后置处理器具体处理过程,不是本文重点,因此这里不进行讲解。

  • 真正进行属性赋值的地方
if (pvs != null) {
			applyPropertyValues(beanName, mbd, bw, pvs);
		}

applyPropertyValues

applyPropertyValues是具体进行属性赋值的地方,我们来看看究竟是如何完成属性的赋值的:
这里会省略不重要的逻辑判断,只挑重点脉络梳理

在开始之前,我们先大概猜测一下该方法完成的逻辑:

  • 因为此时传入applyPropertyValues方法的PropertyValues中保存着当前目标对象所有属性名和其对应已经获得的属性值
  • 下面就是调用BeanWrapper的setPropertyValue方法即可,因为setPropertyValue方法内部会进行类型转换工作
  • 但是还有一点大家可能忘记了,对于字符串类型而言,配置文件可能使用了SPEL表达式,那么我们还需要先一步将这些使用了SPEL表达式的属性进行解析,然后才能进行上面一步操作

当然,上面的逻辑是我的想法,但是Spring具体实现过程却和我想的不太一样:

  • 首先判断传入的PropertyValues是否被标记为已经进行转换了,如果是的话,直接调用bw的setPropertyValues即可
MutablePropertyValues mpvs = null;
		List<PropertyValue> original;

		if (pvs instanceof MutablePropertyValues) {
			mpvs = (MutablePropertyValues) pvs;
			//当前PropertyValues是否已经被转换过了
			if (mpvs.isConverted()) {
				// Shortcut: use the pre-converted values as-is.
				try {
				    //该方法内部会遍历PropertyValues集合,对每个PropertyValue进行属性设置,在设置过程中会进行类型转换工作
					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());
		}
  • 准备工具
//判断用户是否自己设置了转换器,没有的话,使用当前bw
       TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}
		//BeanDefinitionValueResolver负责解析表达式
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
  • 遍历original集合,对每一个PropertyValue都交给valueResolver判断是否需要解析,如果需要的话,就解析
  • 然后再进行类型转换工作
// Create a deep copy, resolving any references for values.
//deepCopy用来存放每一个已经解析好的PropertyValue
		List<PropertyValue> deepCopy = new ArrayList<>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
		//如果当前PropertyValue解析过了,那么就直接加入集合即可
			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);
				}
				// valueResolver对当前属性值进行解析
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				//当前属性可写,并且当前属性名不是嵌套属性的情况下,表示可以对当前属性名进行类型转换
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
				//进行类型转换
					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.
				//如果经过valueResolver解析后的结果和原结果一样,说明没有涉及到表达式的解析
				if (resolvedValue == originalValue) {
					if (convertible) {
					//设置当前属性转换后的结果,该方法还会将converted属性设置为true,表示当前属性转换过了
						pv.setConvertedValue(convertedValue);
					}
					//加入集合
					deepCopy.add(pv);
				}
				//涉及到SPEL表达式相关解析工作
				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));
				}
			}
		}
  • applyPropertyValues方法中convertForProperty进行类型转换的源码如下
@Nullable
	private Object convertForProperty(
			@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
         //区分用户自己设置的 TypeConverter 和原生的BeanWrapperImpl
		if (converter instanceof BeanWrapperImpl) {
			return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
		}
		else {
			PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
			//拿到setter方法参数
			MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
			return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
		}
	}
  • 每个属性解析完了,也转换完了,下面就是设置到bw中了
if (mpvs != null && !resolveNecessary) {
		//设置mpvs为已经转换完成的状态,这样下次再进行来时,就不会进行转换了,例如: 多例的情况
			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);
		}

小结

BeanWrapper在applyPropertyValues中的核心应用就是充当TypeConvert的角色,但是因为考虑到用户可能会自定义TypeConvert,因此是通过AbstractAutowireCapableBeanFactory的convertForProperty方法来完成的类型转换。

  • BeanWrapper的setPropertyValues底层也会对单个propertyValue进行类型转换,这样岂不是重复进行类型转换了吗?

并没有,因为applyPropertyValues方法中,每对一个propertyValue转换后,都会调用setConvertedValue设置转换后的值,该方法内部还会设置当前PropertyValue的converted标记为True。

因此beanWrapper底层调用setPropertyValue时,发现converted标记为True,那么便不会尝试去改PropertyValue进行类型转换了。

相关文章