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

从源码角度去熟悉依赖注入(二)

  • 一、AutowiredFieldElement 注入分析
  • 二、AutowiredMethodElement注入分析
  • 三、doResolveDependency 源码分析
    • 1. @Value 注解解析
      • 测试 ${} 和 #{}
    • 2. resolveMultipleBeans 筛选特殊类型(处理多Bean)
      • 测试
      • findAutowireCandidates 方法解析
        • isAutowireCandidate 源码分析
        • 测试
    • 3. 根据类型找Bean没找着且required=true
    • 4. 寻找唯一Bean(普通注入)
  • 四、总结

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

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

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

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

在这里插入图片描述

一、AutowiredFieldElement 注入分析

注入属性,就是去找到对应Bean,然后通过反射去赋值了,所以咱具体看看 resolveFieldValue(这上篇都有说过):
在这里插入图片描述往里看,可以注意到其解析依赖获取Bean的逻辑是通过 beanFactory#resolveDependency 去实现的,传的参数有俩主要的就是类型转化器和属性描述对象(它是 AutowiredCapableBeanFactory 接口里的一个方法,即使不说,也应该猜到,毕竟这是在属性注入流程里出现的):
在这里插入图片描述上篇说了,AutowiredAnnotationBeanPostProcessor 是有实现 BeanFactoryAware 接口的,那么它在初始化前就会去操作把 BeanFactory 注入到它这里面,注入的是 DefaultListableBeanFactory ,然后它向上转型成了 ConfigurableListableBeanFactory,转不转都一样,ConfigurableListableBeanFactory 接口本就覆盖了所有 BeanFactory 接口系列的功能,这前面也阐述过:
在这里插入图片描述
那也就是说,咱接下来得分析,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 方法。

下面有做懒加载的测试:
在这里插入图片描述可以看见这调试的是一个代理对象,且是 CGLIB 代理:
在这里插入图片描述doResolveDependency 方法是解决属性注入的核心,咱分标题去分析。下面看一下,AutowiredMethodElement的注入,将俩关联起来。

二、AutowiredMethodElement注入分析

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

在这里插入图片描述里面的实现逻辑就是去遍历每一个参数,然后去调用 DefaultListableBeanFactory#resolveDependency 方法获取到对应的Bean,然后放到数组中,最后返回。

在这里插入图片描述至于 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表达式解析,最后都有一个值,然后再交给转换器进行转换得到结果后返回。

测试 ${} 和 #{}

编写对应配置:
在这里插入图片描述引入配置:
在这里插入图片描述
测试:
在这里插入图片描述
在这里插入图片描述

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

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

resolveMultipleBeans 方法对 Stream、数组、Collection、Map 这三种情况进行了处理。
其实处理方式大致都一样,这里随便抽一种类型进行解释,其他的你们能懂。
Map 的处理方式如下:
在这里插入图片描述

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

测试

在这里插入图片描述在这里插入图片描述

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启动的时候会放入写实体放到 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,才会尝试放入返回结果集中,我们直接看最核心的这个方法。
在这里插入图片描述看来看去最后是以 AutowireCandidateResolver#isAutowireCandidate 为返回结果:

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

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

在这里插入图片描述再往上的话就是 SimpleAutowireCandidateResolver 去解析了,就是判断BeanDefinition中的autowireCandidate是不是为true,默认是true的,允许注入:

在这里插入图片描述这是典型的责任链设计模式,其中执行流程是 SimpleAutowireCandidateResolver -》GenericTypeAwareAutowireCandidateResolver -》QualifierAnnotationAutowireCandidateResolver,没前一步都影响后续的执行,相比过滤器那种它只是没有前置处理,也不需要。

测试

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

SimpleAutowireCandidateResolver 主要是去判断 autowireCandidate 是不是为true。
下面我把 autowireCandidate 设置为了 FALSE,那么注入肯定会报错的(报错位置应该是在 doResolveDependency 中找不到对应注入实体,但是 required 又是true):
在这里插入图片描述在这里插入图片描述然后就是 GenericTypeAwareAutowireCandidateResolver 解析器去对泛型的筛选,如下所示:

public class BaseService<O, S> {

	@Autowired
	protected O o;

	@Autowired
	protected S s;

}

在这里插入图片描述BaseService 中定义了泛型,然后 UserService 继承了 BaseService 然后指定了泛型类型,这里就是对这个泛型类型进行筛选。由于这里是泛型,在起初 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 得到的结果是所有的 beanNames,而这里就可以得到泛型类型的真正筛选。

打断点测试:
在这里插入图片描述可以发现要注入的类型是 Object,而筛选出来的结果是所有的 beanName
在这里插入图片描述
而那流程执行结束看结果:
在这里插入图片描述
最后输出:
在这里插入图片描述QualifierAnnotationAutowireCandidateResolver 去解析@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;
}
}

在这里插入图片描述在这里插入图片描述无非就是前后限定符的比对。

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 注解的筛选要看一下,是以值越小优先级越高来判断的:
在这里插入图片描述
其它就没啥好说的。

四、总结

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

  • 这个属性注入呢考虑的是去遍历 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 中的属性注入方法的遍历。

搞个流程图就像如下步骤一样:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/344159.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于网络协议的笔记

简介&#xff1a; 协议&#xff0c; 网络协议的简称&#xff0c;网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连 接、怎么样互相识别等。只有遵守这个约定&#xff0c;计算机之间才能相互通信交流。它的 三要素是&#xff1a;语 法、语义、时序。 为了使数…

水经微图系列产品新功能盘点!

水经微图&#xff0c;简称“微图”。 我们曾在直播中分享过微图APP苹果版的功能&#xff0c;本周四晚19:30我们将在另一个视频号分享盘点微图APP安卓版的详细功能&#xff0c;以及Web版近期上线的新功能功能。 微图APP安卓版 我们在《水经微图安卓版APP正式上线》一文中&…

configure: error: openSSL library not found.解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

初识node.js(使用)

文章目录 项目目录介绍和运行流程1.index.html&#x1f447;2.整个项目的核心入口文件其实是main.js3.App.vue 组件化开发 和 根组件普通组件的注册1.局部注册2.全局注册 综合案例 项目目录介绍和运行流程 1.index.html&#x1f447; <!DOCTYPE html> <html lang&quo…

Offer必备算法_滑动窗口_八道力扣OJ题详解(由浅到深)

目录 滑动窗口算法介绍 ①力扣209. 长度最小的子数组 解析及代码 ②力扣3. 无重复字符的最长子串 解析及代码 ③力扣1004. 最大连续1的个数 III 解析及代码 ④力扣1658. 将x减到0的最小操作数 解析及代码 ⑤力扣904. 水果成篮 解析及代码1&#xff08;使用容器&…

初识SQL注入

目录 注入攻击 SQL注入 手工注入 Information_schema数据库 自动注入 介绍一下这款工具&#xff1a;sqlmap 半自动注入 前面给大家通过学习练习的方式将XSS攻击的几种形式和一些简单的靶场和例题的演示&#xff0c;从本篇开始我将和小伙伴们通过边复习、边练习的方式来进…

2024年购买阿里云服务器多少钱?1000元左右预算可购买的8款云服务器参考

1000元左右预算可以买到哪些配置的阿里云服务器&#xff1f;目前阿里云活动中价格在1000元左右的云服务器有8款&#xff0c;其中经济型e实例云服务器三款&#xff0c;通用算力型u1实例云服务器五款&#xff0c;碰到阿里云有优惠券或者代金券活动时&#xff0c;购买过程中还能使…

Angular组件(一) 分割面板ShrinkSplitter

Angular组件(一) 分割面板ShrinkSplitter 前言 分割面板在日常开发中经常使用&#xff0c;可将一片区域&#xff0c;分割为可以拖拽整宽度或高度的两部分区域。模仿iview的分割面板组件&#xff0c;用angular实现该功能&#xff0c;支持拖拽和[(ngModel)]双向绑定的方式控制区…

Dock的安装部署和基础命令

1 Docker基础 1.1 Docker概述 Docker是一个开源的应用容器引擎&#xff0c;用来运行容器里的运用&#xff0c;可以用来管理容器和镜像的一种工具&#xff0c;基于go语言开发并遵循了apache2.0协议开源。 Docker是在Linux容器里运行应用的开源工具&#xff0c;是一种轻量级的…

Java(TM) Platform SE binary (Process Id: 4412)

Java™ Platform SE binary (Process Id: 4412&#xff09;JAVA8安装过程中出现上述问题win10解决方法 打开任务管理器 在任务管理器中找到详细信息&#xff0c;然后根据上边的进程id找到对应的程序&#xff0c;右键结束任务即可。 在安装jdk17时候&#xff0c;同时出现了上…

05 双向链表

目录 1.双向链表 2.实现 3.OJ题 4.链表和顺序表对比 1. 双向链表 前面写了单向链表&#xff0c;复习一下 无头单向非循环链表&#xff1a;结构简单&#xff0c;一般不会单独用来存数据。实际中更多作为其他数据结构的子结构&#xff0c;如哈希桶、图的邻接等。另外这种结构在…

Pycharm中出现Comparison with None performed with equality operators

此图中警告翻译过来是 &#xff1a;与使用相等运算符执行的None进行比较 这里不应该使用 或者 &#xff01; 而应改为 is 或者 is not

聚观早报 | 华为P70 Art细节曝光;红魔9 Pro龙年限定版官宣

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 1月24日消息 华为P70 Art细节曝光 红魔9 Pro龙年限定版官宣 努比亚Z60 Ultra龙年限定版 小米14 Ultra或没有双版…

创新医疗服务:宠物在线问诊系统的搭建与应用

随着科技的不断进步&#xff0c;创新的医疗服务方式也日渐成为宠物主人关心爱宠健康的首选。本文将深入介绍如何搭建一套创新的宠物在线问诊系统&#xff0c;并展示其应用的技术代码。 1. 系统架构与技术选择 在开始搭建之前&#xff0c;我们需要设计系统的架构并选择合适的…

【Linux工具篇】编辑器vim

目录 vim的基本操作 进入vim(正常模式&#xff09; 正常模式->插入模式 插入模式->正常模式 正常模式->底行模式 底行模式->正常模式 底行模式->退出vim vim正常模式命令集 vim插入模式命令集 vim末行模式命令集 vim操作总结 vim配置 Linux编译器…

网络安全的使命:守护数字世界的稳定和信任

在数字化时代&#xff0c;网络安全的角色不仅仅是技术系统的守护者&#xff0c;更是数字社会的信任保卫者。网络安全的使命是保护、维护和巩固数字世界的稳定性、可靠性以及人们对互联网的信任。本文将深入探讨网络安全是如何履行这一使命的。 第一部分&#xff1a;信息资产的…

使用Sobel算子把视频转换为只剩边缘部分

效果展示 原始视频 修改后的视频 整体代码 import cv2vc cv2.VideoCapture(test.mp4)if vc.isOpened():open, frame vc.read() else:open Falsei 0 while open:ret, frame vc.read()if frame is None:breakif ret True:i 1# 转换为灰度图gray cv2.cvtColor(frame, cv…

复合机器人颠覆传统上下料,实现高效精准生产

在追求高效、精准生产的现代制造业中&#xff0c;传统的上下料方式已经无法满足企业的需求。复合机器人的出现&#xff0c;为制造业带来了革命性的变革。它不仅提高了生产效率&#xff0c;降低了生产成本&#xff0c;还为企业创造了更大的竞争优势。复合机器人的广泛应用&#…

多维时序 | Matlab实现CNN-BiGRU-Mutilhead-Attention卷积双向门控循环单元融合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现CNN-BiGRU-Mutilhead-Attention卷积双向门控循环单元融合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现CNN-BiGRU-Mutilhead-Attention卷积双向门控循环单元融合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一…

GD32E230C8T6《调试篇》之 FMC(闪存)的读写 + USART打印

实验&#xff1a;按键DIG4&#xff08;保存键&#xff09;&#xff0c;任意按下一个数字后&#xff0c;再按保存键写入flash;断电后重新上电&#xff0c;从 flash里读值&#xff0c;显示到数码管 实验工具GD32E230C8T6查看GPIO查看Datasheet 2.6.7章节GPIO 复用 查看用户手册代…
最新文章