Spring 依赖解决过程分析

概述

依赖注入是Spring框架的基本能力。而完成一次依赖注入,概念上又可以分为两步:

  1. 找到依赖
  2. 注入依赖

在这两个步骤中,找到依赖是相对复杂的一个步骤,而一旦找到依赖,注入依赖只是一个简单的设置动作。本文我们就结合源代码分析一下Spring是如何找到依赖的,也就我们所说的依赖解决的过程。

源代码分析

1. 框架对依赖解决的定义

具体来讲,Spring框架首先对依赖注入有接口层面的定义,如下所示 :

    // 接口 : AutowireCapableBeanFactory
	/**
	 * Resolve the specified dependency against the beans defined in this factory.
	 * @param descriptor the descriptor for the dependency (field/method/constructor)
	 * @param requestingBeanName the name of the bean which declares the given dependency
	 * @return the resolved object, or  null if none found
	 * @throws NoSuchBeanDefinitionException if no matching bean was found
	 * @throws NoUniqueBeanDefinitionException if more than one matching bean was found
	 * @throws BeansException if dependency resolution failed for any other reason
	 * @since 2.5
	 * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)
	 */
	@Nullable
	Object resolveDependency(DependencyDescriptor descriptor, 
							@Nullable String requestingBeanName) 
							throws BeansException;

	/**
	 * Resolve the specified dependency against the beans defined in this factory.
	 * @param descriptor the descriptor for the dependency (field/method/constructor)
	 * @param requestingBeanName the name of the bean which declares the given dependency
	 * @param autowiredBeanNames a Set that all names of autowired beans (used for
	 * resolving the given dependency) are supposed to be added to
	 * @param typeConverter the TypeConverter to use for populating arrays and collections
	 * @return the resolved object, or  null if none found
	 * @throws NoSuchBeanDefinitionException if no matching bean was found
	 * @throws NoUniqueBeanDefinitionException if more than one matching bean was found
	 * @throws BeansException if dependency resolution failed for any other reason
	 * @since 2.5
	 * @see DependencyDescriptor
	 */
	@Nullable
	Object resolveDependency(DependencyDescriptor descriptor, 
							@Nullable String requestingBeanName,
							@Nullable Set<String> autowiredBeanNames, 
							@Nullable TypeConverter typeConverter) 
							throws BeansException;

从上面代码可见,接口AutowireCapableBeanFactory定义了两个多态方法resolveDependency用于解析依赖。这些方法涉及了如下参数:

  1. DependencyDescriptor descriptor

    对当前将要注入的依赖的描述符,比如这是一个成员属性依赖,构造函数参数依赖或者成员方法参数依赖等等。

    关于DependencyDescriptor,你可以参考我的另外一篇文章"Spring 工具类 DependencyDescriptor"。

  2. String requestingBeanName

    定义该依赖的bean的名称,也就是将要注入的依赖bean的使用方bean的名称。

  3. Set<String> autowiredBeanNames

    对于所找到的依赖bean,如果使用者需要知道它们的名字,可以传进去这样一个集合,然后依赖解析过程会讲这些依赖bean的名称放入该集合。当然,也设置该参数为null让依赖解析过程不执行此动作。

  4. TypeConverter typeConverter

    如果要处理数组或者集合类型的依赖注入,使用typeConverter进行必要的类型转换。

另外异常方面:
1. 如果针对某个必要依赖找不到匹配的bean会抛出异常NoSuchBeanDefinitionException,
2. 如果针对某个依赖找到多个bean会抛出异常NoUniqueBeanDefinitionException
3. 如果是其他原因依赖解析失败,则会抛出异常BeansException

2. 框架对依赖解决的实现

2.1 resolveDependency(DependencyDescriptor,String)的实现

在具体实现上,Spring框架其实是将resolveDependency(DependencyDescriptor,String)通过resolveDependency(DependencyDescriptor,String,Set<String>,TypeConverter)实现,如下所示:

    // 类 AbstractAutowireCapableBeanFactory 代码片段 
	@Override
	@Nullable
	public Object resolveDependency(DependencyDescriptor descriptor, 
		@Nullable String requestingBeanName) throws BeansException {
		return resolveDependency(descriptor, requestingBeanName, null, null);
	}

只是将第三个参数Set<String> autowiredBeanNames和第四个参数TypeConverter typeConverter都设置为null

接下来,我们来看resolveDependency(DependencyDescriptor,String,Set<String>,TypeConverter)的具体实现。

2.2 resolveDependency(DependencyDescriptor,String,Set<String>,TypeConverter)的实现

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

		// 为依赖描述符设置参数名称发现器 
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
       
		// 下面的 if-else 语句根据 descriptor 中依赖的类型决定如何获取依赖对象
		if (Optional.class == descriptor.getDependencyType()) {
			// 依赖类型是 Optional 的情况 
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
		    // 依赖类型是 ObjectFactory 的情况
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
		    // 依赖类型是 javax.inject.Provider 的情况
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}
		else {
		    // 其他情况,从容器中获取所依赖的bean , 又分为 lazy依赖注入 和 非lazy依赖注入 两种情况
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
              //  非lazy依赖注入的情况
				result = doResolveDependency(descriptor, requestingBeanName, 
						autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

从上面的代码可见,整个依赖解析的过程如下 :

  1. 为依赖描述符设置参数名称发现器;
  2. 根据依赖描述符中携带的依赖类型的不同,执行不同的依赖对象获取分支。

在以上代码的if-else分支中,最常用的分支应该是最后一个包含doResolveDependency调用的分支了,这个分支是从容器中获取所依赖的bean对象。接下来,我们对这一分支中的核心逻辑doResolveDependency进行分析。

2.3 doResolveDependency(DependencyDescriptor,String,Set<String>,TypeConverter)的实现

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

		//  此处涉及知识点 :
		// 1. doResolveDependency 方法会被递归调用,从而导致 ConstructorResolver 被递归使用,
		//    而 ConstructorResolver 使用 ThreadLocal 记录当前注入点,所以它在被递归调用时要首先保存
		//    递归中上一层记录的注入点到 previousInjectionPoint , 在当前层 doResolveDependency 结束
		//    时恢复 previousInjectionPoint 。如果没有该动作,则 ConstructorResolver 会出现错误行为。
		// 2. DependencyDescriptor 继承自 InjectionPoint,
		InjectionPoint previousInjectionPoint = 
			ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			// 某些  DependencyDescriptor 实现子类可能提供了快捷解析方式,所以这里先调用该方法尝试快捷解析,
			// 而 DependencyDescriptor 类对该方法的缺省实现返回 null
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				// 快捷实现能够解析到依赖的情况,直接返回
				return shortcut;
			}

			Class<?> type = descriptor.getDependencyType();
			// 看看有没有注解@Value提供的值  
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			if (value != null) {
				// 有注解@Value提供值的情况
				if (value instanceof String) {
					// 现在 value 只是个字符串表达式,现在需要尝试对其求值,主要是使用具体值
					// 替换其中的占位符
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ?
							getMergedBeanDefinition(beanName) : null);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				// 进行必要的类型转换并返回
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					// A custom TypeConverter which does not support TypeDescriptor resolution...
					return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
				}
			}

			// 针对依赖是 stream, 数组,Collection, Map 等类型的情况进行依赖bean的分析,
			// 所返回的对象是 stream,数组,Collection, Map 这样类型的对象,其中的每个元素的类型
			// 由依赖描述符中的类型决定,该方法调用中真正用到了 typeConverter 进行必要的类型转换
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, 
					autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			// 针对依赖是单个 bean 依赖的情况进行依赖bean的分析  
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				if (isRequired(descriptor)) {
					// 依赖被标记为 required, 但是却没有找打任何匹配的bean,
					// 抛出异常 NoSuchBeanDefinitionException
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;

			if (matchingBeans.size() > 1) {
				// 依赖是单个 bean 依赖,但是却找到了多个依赖bean候选,
				// 此时参考 @Primary,@Priority 等注解信息决定使用哪个bean作为最终要使用的依赖bean,
				// 如果这些注解不存在,该步骤返回 null
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					// 参考@Primary,@Priority 等注解信息之后仍然分析不出合适的依赖bean,
					// 此时看依赖是否被标记为 required 或者 不对应多个依赖bean,
					// 如果是这些情况,使用依赖描述符自身的 resolveNotUnique 方法尝试解决,
					// 缺省是抛出异常 NoUniqueBeanDefinitionException
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(descriptor.getResolvableType(), 
							matchingBeans);
					}
					else {
						// In case of an optional Collection/Map, silently ignore a non-unique case:
						// possibly it was meant to be an empty collection of multiple regular beans
						// (before 4.3 in particular when we didn't even look for collection beans).
						return null;
					}
				}
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				//  依赖是单个 bean 依赖,正好也只找到了一个候选依赖bean的情况
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

			if (autowiredBeanNames != null) {
				// 调用者提供了不为 null 的参数 autowiredBeanNames ,
				// 表明需要向调用者通过此参数返回所找到的依赖的 bean 的名称
				autowiredBeanNames.add(autowiredBeanName);
			}
			if (instanceCandidate instanceof Class) {
				// 其实是调用 beanFactory.getBean(autowiredBeanName)  触发获取所依赖的bean的实例
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
				// 此时如果发现所找到的候选bean其实是一个特殊的bean NullBean,但依赖又被设定为 required,
				// 这里仍旧抛出异常 NoSuchBeanDefinitionException
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				// 此时如果发现所找到的候选bean其实是一个特殊的bean NullBean,但依赖又没被设定为 required,  
				// 此时返回 null
				result = null;
			}
			// 类型检查,如果类型不匹配抛出异常 BeanNotOfRequiredTypeException  
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, 
					instanceCandidate.getClass());
			}
			// 返回最终确定要是用的依赖bean  
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

参考文章

Spring 工具类 DependencyDescriptor

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值