【Spring】深入解析 bean 的属性填充 二 AutowiredFieldElement#inject

前言

接上文,本章重点解读 InjectedElement#inject

  • 针对属性注入的 AutowiredFieldElement#inject
  • 针对方法注入的 AutowiredMethodElement#inject

AutowiredFieldElement#inject

	@Override
	protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Field field = (Field) this.member;
		Object value;
		
		// 已缓存
		if (this.cached) {
			value = resolvedCachedArgument(beanName, this.cachedFieldValue);
		}
		else {
			// 构造 DependencyDescriptor
			DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
			desc.setContainingClass(bean.getClass());
			Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
			
			// 获取 TypeConverter
			TypeConverter typeConverter = beanFactory.getTypeConverter();
			try {
				// 委托给 beanFactory.resolveDependency 方法获取
				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
			}
			catch (BeansException ex) {
				
			}
			
			// 缓存...
			
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

AutowiredFieldElement#inject 就是针对属性的注入:

  • DependencyDescriptor 是对属性依赖的描述类,封装诸如当前属性的信息、必填性等
  • 属性的获取与 byType 填充方式相似,最终也是委托给 AutowireCapableBeanFactory#resolveDependency 方法,它的核心实现在 DefaultListableBeanFactory 中,接下来我们就重点解读该方法
  • 最后基于反射给属性赋值

DefaultListableBeanFactory#resolveDependency

	@Override
	@Nullable
	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		// 参数名称发现器
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

		/**
		 * 此处是针对不同的类型对返回值进行处理,但核心逻辑依然是收口
		 * 		到 doResolveDependency 方法
		 */
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			// ...
		}
		/**
		 * 此分支才是我们最常用的,核心逻辑在 doResolveDependency
		 */
		else {
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}
  • 这里针对不同类型的元素,对返回结果进行对应处理
  • 最终的核心逻辑还是收口在 doResolveDependency 方法

doResolveDependency

	@Nullable
	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			// ShortcutDependencyDescriptor 处理,即缓存处理
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				return shortcut;
			}

			Class<?> type = descriptor.getDependencyType();
			// 如果是 @Value 注解元素,则获取 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);
					// SpEL 解析
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				// 类型转换后返回
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					
				}
			}

			/**
			 * 对于返回类型是 Array Collection Map 类型的属性,
			 * 会对基于 findAutowireCandidates 获取的实例进行类型转换后直接返回
			 */
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			/**
			 * 核心方法 findAutowireCandidates:获取所有符合
			 * 		注入条件的实例
			 */
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

			// 如果没有,抛出异常 expected at least 1 bean which qualifies as autowire candidate.
			if (matchingBeans.isEmpty()) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;

			/**
			 * 如何元素个数多余一个,则委托 determineAutowireCandidate 方法
			 * 		进行再次筛选
			 * 如果筛选结果为空,但接受元素不是集合类型,则抛出异常
			 * 如果筛选出来了,那么就最终返回
			 */
			if (matchingBeans.size() > 1) {
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
					}
					else {
		
						return null;
					}
				}
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

			// ...
			
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

核心逻辑实现,结合代码注释,这里再做个总结:

  • 之前会对已经解析的属性缓存对应的 ShortcutDependencyDescriptor,此处会有体现
  • 正片开始,第一步是解析 @Value 注解,这里有几个核心步骤,比如 占位符的解析,它让 @Value 注解支持从 Environment 中直接获取对应属性,再比如 SpEL 表达式的解析,不仅支持各种常见表达式解析,甚至如同我们之前演示的示例获取容器中的 bean示例@Value("#{a}")
  • 接下来就是 @Autowired 注解的处理,首先针对集合类型的属性,会直接对 findAutowireCandidates 获取的备选属性进行类型转换后直接返回,这就是之前的示例中为什么我们可以注入此类属性 Map<String, BasicPOJO> map
  • 其中 findAutowireCandidates 方法用来返回所有满足注入条件的示例,下文会重点解读
  • 接下来,如果根本没有备选实例且必须注入,则抛出异常
  • 反之如果有多个备选示例,则由 determineAutowireCandidate 尝试确定唯一实例,下文会单独解读
  • 如果最终可以确定唯一元素,则成功返回

findAutowireCandidates

	protected Map<String, Object> findAutowireCandidates(
			@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

		// 获取所有指定类型的 beanName(包括父级BeanFactory)
		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
		/**
		 * 对于注册在 resolvableDependencies 中的元素,此处会加入备选
		 * 容器中默认注册的元素如下:
		 * beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		 * beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		 * beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		 * beanFactory.registerResolvableDependency(ApplicationContext.class, this);
		 */
		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;
				}
			}
		}
		/**
		 * 对于
		 * 1)不是自引用(大概就是自己注入自己这种情况)
		 * 并且 2)基于 AutowireCandidateResolver 进行筛选
		 * 通过的元素加入备选
		 */
		for (String candidate : candidateNames) {
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
		/**
		 * 如果第一次筛选没有没有备选实例,此处会重新生成 DependencyDescriptor
		 * 		再次筛选,针对该 DependencyDescriptor 会放弃泛型精确匹配
		 */
		if (result.isEmpty()) {
			boolean multiple = indicatesMultipleBeans(requiredType);
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
			for (String candidate : candidateNames) {
				if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
						(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
			// 若仍旧没有备选示例,则考虑自引用
			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;
	}

该方法略长,结合注释进行概括:

  • 对于注册在 resolvableDependencies 中的元素,此处会加入备选,容器会在启动阶段注册一些默认的 ResolvableDependency 进去,见注释,可以理解为几组默认的 类型 -> bean实例
  • 第一轮筛选:1)不能是自引用,即自身注入自身 2)交给 AutowireCandidateResolver 进一步筛选,此处会进行诸如 泛型 限定符 的匹配,下一章节详细解读
  • 若筛选结果为空,则放弃泛型的精确匹配再筛选一次
  • 若筛选结果还为空,则考虑自引用
  • 最后无论如何返回即可

可以发现,核心的筛选逻辑由 AutowireCandidateResolver 实现,诸如示例中针对泛型的精确匹配,再比如 @Qualifier 注解的限定符匹配等

determineAutowireCandidate

	@Nullable
	protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
		Class<?> requiredType = descriptor.getDependencyType();
		
		// 筛选出被 @Primary 注解修饰的实例
		String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
		if (primaryCandidate != null) {
			return primaryCandidate;
		}
		
		// 根据实现的 Priority 接口进行优先级过滤
		String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
		if (priorityCandidate != null) {
			return priorityCandidate;
		}
		
		/**
		 * 最后,如果是 resolvableDependencies 则优先返回
		 * 否则进行 name 匹配
		 */
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateName = entry.getKey();
			Object beanInstance = entry.getValue();
			if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
					matchesBeanName(candidateName, descriptor.getDependencyName())) {
				return candidateName;
			}
		}
		return null;
	}

之前提到,对于返回多个备选实例的情况,若接受属性非集合,则要进行优先级筛选:

  • 若实例被 @Primary 注解修饰,则优先
  • 根据实现的 Priority 接口进行优先级筛选
  • 最后,才会根据 beanName 和元素名称进行匹配,这就是为什么示例配置中 BasicPOJO a 属性会成功注入

总结

整体概括一下:

基于 @Autowired 注解驱动的依赖注入类似于 byType 自动依赖注入的方式,因而必然存在多个匹配的情况,当接受属性是集合数组等类型如 Map Array 时可以全部注入,相反地对于单个属性,则依次进行诸如 @Primary 属性名称 等的匹配

其中 DefaultListableBeanFactory#findAutowireCandicate 方法获取所有备选实例,其中重要的筛选逻辑诸如 泛型匹配 限定符匹配 等是委托给 AutowireCandidateResolver 实现的,这也是我们下章重点解读的对象

基于 @Resource 注解驱动的依赖注入类似于 byName 自动依赖注入的方式,相对类型的注入
逻辑会清晰一点,毕竟 name 跟 bean 理论上一对一,就不涉及复杂的筛选逻辑了

参考

【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析

上一篇:【Spring】深入解析 bean 的属性填充 一 autowireByName autowireByType

下一篇:【Spring】深入解析 bean 的属性填充 三 AutowireCandidateResolver

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值