【Spring源码分析】从源码角度去熟悉依赖注入(二)

这篇具有很好参考价值的文章主要介绍了【Spring源码分析】从源码角度去熟悉依赖注入(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

阅读此需阅读下面这些博客先
【Spring源码分析】Bean的元数据和一些Spring的工具
【Spring源码分析】BeanFactory系列接口解读
【Spring源码分析】执行流程之非懒加载单例Bean的实例化逻辑
【Spring源码分析】从源码角度去熟悉依赖注入(一)

上篇这里简单提一下哈,怕有些没看的,这是连着的…:

  • 上篇是提了下生命周期的属性注入阶段,也就是 populateBean 的具体实现;
  • 然后里面涉及到实例化后阶段,和三种注入方式,Spring的BYNAME和BYTYPE还有通过MergedBeanDefinitionPostProcessor 去重定义 BeanDefinition 时的注入这俩种是简单提了一下,然后就准备去详细分析一下使用@Autowired注解是如何注入的呢?这就涉及到 InstantiationAwareBeanPostProcessor 的实现类 AutowiredAnnotationBeanPostProcessor 里的具体实现了;
  • 然后就去简单分析了一下是注入的,首先是去遍历属性和方法,看谁有 @Autowired、@Value 这俩注解,有就创建对应的 InjectElement 然后进行注入。

这篇呢主要就是去分析一下 AutowiredFieldElement 和 AutowiredMethodElement 里注入的具体实现。
也是对应着下面流程图中的 如何去寻找Bean的?

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql

一、AutowiredFieldElement 注入分析

注入属性,就是去找到对应Bean,然后通过反射去赋值了,所以咱具体看看 resolveFieldValue(这上篇都有说过):
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql往里看,可以注意到其解析依赖获取Bean的逻辑是通过 beanFactory#resolveDependency 去实现的,传的参数有俩主要的就是类型转化器和属性描述对象(它是 AutowiredCapableBeanFactory 接口里的一个方法,即使不说,也应该猜到,毕竟这是在属性注入流程里出现的):
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql上篇说了,AutowiredAnnotationBeanPostProcessor 是有实现 BeanFactoryAware 接口的,那么它在初始化前就会去操作把 BeanFactory 注入到它这里面,注入的是 DefaultListableBeanFactory ,然后它向上转型成了 ConfigurableListableBeanFactory,转不转都一样,ConfigurableListableBeanFactory 接口本就覆盖了所有 BeanFactory 接口系列的功能,这前面也阐述过:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
那也就是说,咱接下来得分析,DefaultListableBeanFactory#resolveDependency,在分析之前得清除,这是 DefaultListableBeanFactory 下的方法实现,而传的参数 DependencyDescriptor 也是注入点的子类,就是说其可以描述构造参数、属性、方法参数,即该方法不管是注入的公共方法,包括属性和方法(所以如果碰到关系方法的地方不要懵,比如下面的第一行)。

	@Override
	@Nullable
	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
		// 用来获取方法入参名字的
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

		// 所需要的类型是Optional
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		// 所需要的的类型是ObjectFactory,或ObjectProvider
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}
		else {
			// 在属性或set方法上使用了@Lazy注解,那么则构造一个代理对象并返回,真正使用该代理对象时才进行类型筛选Bean
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);

			if (result == null) {
				// descriptor表示某个属性或某个set方法
				// requestingBeanName表示正在进行依赖注入的Bean
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

  • 首先是获取到参数的名字,这肯定是字节码层面的,咱别去细究了;
  • 然后是去对类型进行判断,Optional、ObjectFactory、ObjectProvider、javax下的那Provider,这些俺几乎不用,不去详细看了,直接看else的逻辑;
  • 判断参数上是不是加了@Lazy注解,如果加了表示该Bean注入进行懒加载,这样就话返回的就是一个 CGLIB 的动态代理对象。
    • 在 Mybatis 源码分析里,咱遇到过Javassit、jdk、CGLIB 三种动态代理,这里为什么选择 CGLIB原因如下:它不是运行时候用的,它是直接Spring加载的时候进行的,所以不需要Javassit 去做,而jdk动态代理是在接口层面的,属性注入肯定不仅仅是类,所以毫无疑问 CGLIB 是这里最好的选择了
  • 如果没用 @Lazy 的话,就是正常构造了,调用 doResolveDependency 方法。

下面有做懒加载的测试:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql可以看见这调试的是一个代理对象,且是 CGLIB 代理:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sqldoResolveDependency 方法是解决属性注入的核心,咱分标题去分析。下面看一下,AutowiredMethodElement的注入,将俩关联起来。

二、AutowiredMethodElement注入分析

有关方法方式的注入,主要是在 resolveMethodArguments 方法中进行:

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql里面的实现逻辑就是去遍历每一个参数,然后去调用 DefaultListableBeanFactory#resolveDependency 方法获取到对应的Bean,然后放到数组中,最后返回。

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql至于 resolveDependency 的实现逻辑,就和上面串起来了。

三、doResolveDependency 源码分析

(吐槽一下,本来就是这部分注入流程都在这里,我分析源码解读算细了,但CSDN md 恶心人,昨天写的今天打开草稿一看全没了,我是真服了)
先给出源码,再针对源码各部分依次解读:

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

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			// 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存
			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);

					// 解析Spring表达式(#{})
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				// 将value转化为descriptor所对应的类型
				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()));
				}
			}

			// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			// 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				// required为true,抛异常
				// 找不到对应的Bean,但是 required 又是 true,就会抛出异常,@Autowired 的 required 默认是 true 的
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;

			if (matchingBeans.size() > 1) {
				// 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					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 {
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

			// 记录匹配过的beanName
			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			// 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBean
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

1. @Value 注解解析

在判定字段和方法是否为注入点时,除了判定 @Autowired 注解还有就是 @Value 注解。而这里就是对 @Value 注解进行解析:

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

					// 解析Spring表达式(#{})
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				// 将value转化为descriptor所对应的类型
				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()));
				}
			}

解析过程很简单,就是拿到 @Value 注解中的内容,若是 ${} 开头就去 Environment 中去寻其结果。要是 #{} 开头就是用 SPEL 表达式解析器去解析。
不管是从环境对象(配置文件、系统配置、总环境配置)中去找还是SPEL表达式解析,最后都有一个值,然后再交给转换器进行转换得到结果后返回。

测试 ${} 和 #{}

编写对应配置:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql引入配置:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
测试:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql

2. resolveMultipleBeans 筛选特殊类型(处理多Bean)

			// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

resolveMultipleBeans 方法对 Stream、数组、Collection、Map 这三种情况进行了处理。
其实处理方式大致都一样,这里随便抽一种类型进行解释,其他的你们能懂。
Map 的处理方式如下:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql

  • 主要就是去找到 Map 上对应的泛型类型;
  • 然后通过Value泛型的类型去调用 findAutowireCandidates 方法去根据类型找对应的Beans,返回的是一个 Map<String,Object> 其中key 是 beanName,Value 是对应的Bean。
  • 若是其他类型的话就这个Map进行些处理,然后返回结果集就好了,这 Map 是不需要处理的,然后就直接返回了。

测试

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql

findAutowireCandidates 方法解析

这个方法很重要,也很难,主要概括下就是通过类型去找对应Bean的Map集合,Key 是 BeanName,Value 的话是对应的Bean或者是对应的Class对象。

先给出源码的解析:

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

		// 从BeanFactory中找出和requiredType所匹配的beanName,仅仅是beanName,这些bean不一定经过了实例化,只有到最终确定某个Bean了,如果这个Bean还没有实例化才会真正进行实例化
		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);

		// 根据类型从resolvableDependencies中匹配Bean,resolvableDependencies中存放的是类型:Bean对象,比如BeanFactory.class:BeanFactory对象,在Spring启动时设置
		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;
				}
			}
		}


		for (String candidate : candidateNames) {
			// 如果不是自己,则判断该candidate到底能不能用来进行自动注入
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}

		// 为空要么是真的没有匹配的,要么是匹配的自己
		if (result.isEmpty()) {
			// 需要匹配的类型是不是Map、数组之类的
			boolean multiple = indicatesMultipleBeans(requiredType);
			// Consider fallback matches if the first pass failed to find anything...
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
			for (String candidate : candidateNames) {
				if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
						(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}

			// 匹配的是自己,被自己添加到result中
			if (result.isEmpty() && !multiple) {
				// Consider self references as a final pass...
				// but in the case of a dependency collection, not the very same bean itself.
				for (String candidate : candidateNames) {
					if (isSelfReference(beanName, candidate) &&
							(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
							isAutowireCandidate(candidate, fallbackDescriptor)) {
						addCandidateEntry(result, candidate, descriptor, requiredType);
					}
				}
			}
		}
		return result;
	}

咱先对整体的流程进行个概述:

  • BeanFactoryUtils.beanNamesForTypeIncludingAncestors 是去根据类型去找到对应的 beanNames,这个是真正的根据类型去找。内部实现概述起来不难,就是去遍历所有的beanNames,如果单例池中有就拿bean出来,没有就去找对应的beanDefinition,那里面在扫描过程中有把Class实体存进去,那么直接 instanceOf 就知道合不合要求了。

    • 【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
  • 下一步就是去遍历Spring启动的时候会放入写实体放到 resolvableDependencies 下,就是这些类型的话也是可以被注入的,我所说的有如下几个:

    • beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
      beanFactory.registerResolvableDependency(ResourceLoader.class, this);
      beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
      beanFactory.registerResolvableDependency(ApplicationContext.class, this);
  • 对注入的不是自己再度进行筛选,通过的 isAutowireCandidate 方法,这里用了责任链的设计模式,下面会再度概述这点。

  • 如果筛选到最后没一个符合的,就会去判断是否引用的自己,是的话放入返回的结果集中。

isAutowireCandidate 源码分析

这个是 findAutowireCandidates 中的第三点,前俩点主要是找可以注入的实体,只有第三点是会进行再次筛选

isAutowireCandidate 这个方法是一个重载方法,只有它返回为true,才会尝试放入返回结果集中,我们直接看最核心的这个方法。
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql看来看去最后是以 AutowireCandidateResolver#isAutowireCandidate 为返回结果:

在分析之前,还是得看一下这个关系图;
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql源码中所使用的 AutowireCandidatesResolver 实体就是 QualifierAnnotationAutowireCandidateResolver 实体对象,接下来咱就看看它的 #isAutowireCandidate 的实现:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql它首先是去父类的筛选,若父类筛选不通过的话就直接返回FALSE了,不会执行if里的代码。
而 if 里面的代码就是对 @Qualifier 注解(限定符)的筛选,若原实体和注入字段上有@Qualifier注解就去比对里面的Value值

父类 GenericTypeAwareAutowireCandidateResolver 解析也是一样,先交给父类,然后再尝试自己执行,这个是去解析泛型的,一点复杂,不解析了。

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql再往上的话就是 SimpleAutowireCandidateResolver 去解析了,就是判断BeanDefinition中的autowireCandidate是不是为true,默认是true的,允许注入:

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql这是典型的责任链设计模式,其中执行流程是 SimpleAutowireCandidateResolver -》GenericTypeAwareAutowireCandidateResolver -》QualifierAnnotationAutowireCandidateResolver,没前一步都影响后续的执行,相比过滤器那种它只是没有前置处理,也不需要。

测试

对上面的三种过滤形式进行个简单测试好理解:

SimpleAutowireCandidateResolver 主要是去判断 autowireCandidate 是不是为true。
下面我把 autowireCandidate 设置为了 FALSE,那么注入肯定会报错的(报错位置应该是在 doResolveDependency 中找不到对应注入实体,但是 required 又是true):
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql然后就是 GenericTypeAwareAutowireCandidateResolver 解析器去对泛型的筛选,如下所示:

public class BaseService<O, S> {

	@Autowired
	protected O o;

	@Autowired
	protected S s;

}

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sqlBaseService 中定义了泛型,然后 UserService 继承了 BaseService 然后指定了泛型类型,这里就是对这个泛型类型进行筛选。由于这里是泛型,在起初 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 得到的结果是所有的 beanNames,而这里就可以得到泛型类型的真正筛选。

打断点测试:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql可以发现要注入的类型是 Object,而筛选出来的结果是所有的 beanName
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
而那流程执行结束看结果:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
最后输出:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sqlQualifierAnnotationAutowireCandidateResolver 去解析@Qualifier我觉得开发过的对这个应该都不陌生的。
无非就是去标志一下,最后注入的时候需要比对一下:
比如下面提供几种负载均衡策略实体,然后通过注解自由注入例子:

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("random")
public @interface Random {
}
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("roundRobin")
public @interface RoundRobin {
}
public interface LoadBalance {
String select();
}
@Component
@Random
public class RandomStrategy implements LoadBalance {
@Override
public String select() {
return null;
}
}
@Component
@RoundRobin
public class RoundRobinStrategy implements LoadBalance {
@Override
public String select() {
return null;
}
}

【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql无非就是前后限定符的比对。

3. 根据类型找Bean没找着且required=true

			// 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				// required为true,抛异常
				// 找不到对应的Bean,但是 required 又是 true,就会抛出异常,@Autowired 的 required 默认是 true 的
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

通过 findAutowireCandidates 要是没有找到结果集的话,就说明没有匹配的 Bean,那么这时required又是true,默认就是true,那么这时会抛出异常。

4. 寻找唯一Bean(普通注入)

现在就是我们最常见的注入了,就是普通的注入,前面奇奇怪怪的都被筛选光了。

			String autowiredBeanName;
			Object instanceCandidate;

			if (matchingBeans.size() > 1) {
				// 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					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 {
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

这里的话会先进行判断,若 findAutowireCandidates 返回的结果集是多个那就进行再处理,若是一个则没啥好说的,直接返回到时候反射赋值就是了。那咱就看看它多个的情况下又会做哪些处理吧,主要是在 determineAutowireCandidate 方法中。

这个方法实现不难,主要就是三层筛选,先@Primary,再@Priority,前俩者都没的话就根据名字了

	@Nullable
	protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
		Class<?> requiredType = descriptor.getDependencyType();
		// candidates表示根据类型所找到的多个Bean,判断这些Bean中是否有一个是@Primary的
		String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
		if (primaryCandidate != null) {
			return primaryCandidate;
		}

		// 取优先级最高的Bean
		String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
		if (priorityCandidate != null) {
			return priorityCandidate;
		}

		// Fallback
		// 匹配descriptor的名字,要么是字段的名字,要么是set方法入参的名字
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateName = entry.getKey();
			Object beanInstance = entry.getValue();

			// resolvableDependencies记录了某个类型对应某个Bean,启动Spring时会进行设置,比如BeanFactory.class对应BeanFactory实例
			// 注意:如果是Spring自己的byType,descriptor.getDependencyName()将返回空,只有是@Autowired才会方法属性名或方法参数名
			if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
					matchesBeanName(candidateName, descriptor.getDependencyName())) {
				return candidateName;
			}
		}
		return null;
	}

@Priority 注解的筛选要看一下,是以值越小优先级越高来判断的:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql
其它就没啥好说的。

四、总结

这简单做个小结吧,注意这里是和上一节是一起的:

  • 这个属性注入呢考虑的是去遍历 InstantiationAwareBeanPostProcessor 中的属性注入方法调用,其中有个 AutowiredAnnotationBeanPostProcessor 就是我们所分析的。
  • 首先呢就是去遍历属性遍历方法寻找注入点,找到后封装成一个注入点实体存进集合中;
  • 遍历这个集合依次进行注入,要是方法的话就参数遍历个遍去调用 doResolverDependency 方法获取要注入的bean,最后合一起反射调用方法就是了,属性的话,更简单,获取后反射直接赋值。
  • doResolveDependency逻辑:
    • 先进行 @Value 注解的注入;
    • 筛选Map、Stream、Collection、数组这特殊类型;
    • findAutowireCandidates 去根据类型筛选Bean,其中筛选有 autowireCandidate 需要为 true,筛选泛型类型,筛选 @Qualifier 限定符匹配的
    • 若有多Bean需要注入还需要进行筛选:
      • @Primary
      • @Priority(注意这里是值越小优先级越高,且值不能一样,不然抛异常的)
      • 属性名或者说是参数名和 beanName 匹配一个出来。
  • 反射调用/反射赋值(完成依赖注入!!!)

筛选6步记住:@Value ---- autowireCandidate 元数据属性要为 true ----- @Qualifier 限定符若有需匹配 ------- 多Bean的话 @Primary --------@Priority ------属性名或者参数名匹配

下一篇的话把 @Resource 注解注入源码也分析一下,其实可以猜到,也得通过 InstantiationAwareBeanPostProcessor 中的属性注入方法的遍历。

搞个流程图就像如下步骤一样:
【Spring源码分析】从源码角度去熟悉依赖注入(二),Java源码分析,spring,数据库,sql文章来源地址https://www.toymoban.com/news/detail-824369.html

到了这里,关于【Spring源码分析】从源码角度去熟悉依赖注入(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring-1-深入理解Spring XML中的依赖注入(DI):简化Java应用程序开发

    前两篇文章我们介绍了什么是Spring,以及Spring的一些核心概念,并且快速快发一个Spring项目,以及详细讲解IOC,今天详细介绍一些DI(依赖注入) 能够配置setter方式注入属性值 能够配置构造方式注入属性值 能够理解什么是自动装配 思考:向一个类中传递数据的方式有几种?(给类

    2024年02月13日
    浏览(50)
  • Java Spring IoC&DI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性

    💓 博客主页:从零开始的-CodeNinja之路 ⏩ 收录文章:Java Spring IoCDI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性 🎉欢迎大家点赞👍评论📝收藏⭐文章 我们一下要学习的内容都是为了实现⾼内聚低耦合来进行的 软件设计原则:⾼内聚低耦合. ⾼内聚指

    2024年04月15日
    浏览(47)
  • Spring 02 -Spring依赖注入+Spring注解开发

    依赖注入:在Spring创建对象的同时,为其属性赋值,称之为依赖注入。 创建对象时,Spring工厂会通过Set方法为对象的属性赋值。 范例:定义一个Bean类型 属性注入 范例:定义一个Bean类型 提供构造方法 构造方法注入 复杂类型指的是:list、set、map、array、properties等类型 定义

    2023年04月09日
    浏览(47)
  • Spring面试整理-Spring的依赖注入

    Spring框架的依赖注入(DI)是其核心功能之一,它允许对象定义它们依赖的其他对象,而不是自己创建或查找它们。这种机制促进了松耦合和更容易的测试。 依赖注入是一种设计模式,其中一个对象或方法提供另一个对象的依赖关系。在Spring中,这些依赖通常是服务、配置值

    2024年01月19日
    浏览(60)
  • Spring IOC:详解【依赖注入数值问题 & 依赖注入方式】

    编译软件:IntelliJ IDEA 2019.2.4 x64 操作系统:win10 x64 位 家庭版 Maven版本:apache-maven-3.6.3 Mybatis版本:3.5.6 spring版本:5.3.1 第一章:初识Spring:如何在Maven工程上搭建Spring框架? 第二章:Spring IOC:IOC在Spring底层中如何实现? 第三章:Spring IOC:详解【依赖注入数值问题 依赖注入

    2024年02月04日
    浏览(49)
  • spring——依赖注入原理及注入方式

    🟣1.依赖注入(Dependency Injection,DI) 是一种设计模式和编程技术,其原理是将对象的依赖关系由外部容器来管理和注入。它的目的是解耦组件之间的依赖关系,提高代码的灵活性、可维护性和可测试性。 🟣2.依赖注入的原理 是通过在对象的构造函数、属性或方法中注入所依

    2024年02月08日
    浏览(42)
  • Spring DI简介及依赖注入方式和依赖注入类型

    目录 一、什么是依赖注入 二、依赖注入方式 1. Setter注入 2. 构造方法注入 3. 自动注入  三、依赖注入类型 1. 注入bean类型 2. 注入基本数据类型 3. 注入List集合 4. 注入Set集合 5. 注入Map集合 6. 注入Properties对象 往期专栏文章相关导读  1. Maven系列专栏文章 2. Mybatis系列专栏文章

    2024年02月02日
    浏览(47)
  • Spring 依赖注入详解

    目录 1、基于构造器的依赖注入 2、基于 Setter 方法的依赖注入 3、使用构造器注入还是 setter 方法注入? 4、依赖注入解析的过程 5、依赖注入的相关示例 // 依赖关系,指的就是对象之间的相互协作关系         依赖注入(DI)是一个过程,在这个过程中,对象仅通过构造函

    2024年02月06日
    浏览(51)
  • Spring 的依赖注入

    @ 目录 Spring 的依赖注入 每博一文案 1. 依赖注入 1.1 构造注入 1.1.1 通过参数名进行构造注入 1.1.2 通过参数的下标,进行构造注入 1.1.3 不指定参数下标,不指定参数名字,通过自动装配的方式 1.2 set 注入 2. set注入的各种方式详解 2.1 set 注入外部Bean 2.2 set 注入内部Bean 2.3 set 注

    2024年02月16日
    浏览(46)
  • 4、Spring之依赖注入

    依赖注入就是对类的属性进行赋值 创建名为spring_ioc_xml的新module,过程参考3.1节 注意:constructor-arg标签的数量,必须和某一个构造器方法的参数数量一致 4.4.1.1、配置bean 注意:该property name=\\\"sex\\\" value=\\\"null\\\"写法,实际为sex所赋的值是字符串null 4.4.1.2、测试 由控制台日志可知,

    2024年02月14日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包