Spring 注解原理(三)@Qualifier @Value

Spring 注解原理(三)AutowireCandidateResolver:@Qualifier @Value @Autowire @Lazy

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

1. AutowireCandidateResolver 接口

AutowireCandidateResolver 用来判断一个给定的 bean 是否可以注入,最主要的方法是 isAutowireCandidate。简单来说 isAutowireCandidate 就根据 @Qualifier 添加过滤规则来判断 bean 是否合法。

public interface AutowireCandidateResolver {
// 判断给定的 bdHolder 是否可以注入 descriptor,BeanDefinition#autowireCandidate 默认为 true
// DependencyDescriptor 是对字段、方法、参数的封装,便于统一处理,这里一般是对属性写方法参数的封装
default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
} // @since 5.0 判断是否必须注入,如果是字段类型是 Optional 或有 @Null 注解时为 false
default boolean isRequired(DependencyDescriptor descriptor) {
return descriptor.isRequired();
}
// @since 5.1 判断是否有 @Qualifier(Spring 或 JDK) 或自定义的注解
default boolean hasQualifier(DependencyDescriptor descriptor) {
return false;
} // @Value 时直接返回
default Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
// @since 4.0
default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}
}

1.1 AutowireCandidateResolver 类图

AutowireCandidateResolver 的实现有以下几个:

Spring 注解原理(三)@Qualifier @Value

  • SimpleAutowireCandidateResolver 相当于一个简单的适配器。
  • GenericTypeAwareAutowireCandidateResolver 进一步判断泛型是否匹配。
  • QualifierAnnotationAutowireCandidateResolver 处理 @Qualifier 和 @Value 注解。
  • ContextAnnotationAutowireCandidateResolver 处理 @Lazy 注解,重写了 getLazyResolutionProxyIfNecessary 方法。

1.2 核心方法说明

(1)isAutowireCandidate 方法重载说明:

  • SimpleAutowireCandidateResolver:直接判断 bd.autowireCandidate=true,默认为 true,也就是可以注入。
  • GenericTypeAwareAutowireCandidateResolver:继续检查依赖的类型 dependencyType 和实际注入的类型 targetType 上的泛型是否匹配。核心方法:checkGenericTypeMatch。
  • QualifierAnnotationAutowireCandidateResolver:继续校验 @Qualifier 规则是否匹配成功。核心方法:checkQualifiers。

(2)getSuggestedValue 方法重载说明:

  • SimpleAutowireCandidateResolver:直接判断 descriptor.required=true,默认为 true。也就是不能注入时抛出异常。如果是 Optional 类型时会修改 descriptor.required=false。
  • QualifierAnnotationAutowireCandidateResolver:继续判断 @Autowire 注解,仅当 @Autowire(required=false) 时,返回 false。如果不存在 @Autowire 或未指定 required 属性都会返回 true。

(3)isRequired 方法重载说明:

  • SimpleAutowireCandidateResolver:直接返回 nul。
  • QualifierAnnotationAutowireCandidateResolver:读取 @Value 注解的 value 属性值,作为指定值注入。

(4)getLazyResolutionProxyIfNecessary 方法说明:

  • ContextAnnotationAutowireCandidateResolver:如果标注 @Lazy 注解,会生成一个代理对象。只有使用到该对象时才会真正调用 beanFactory#doResolveDependency 查找依赖,其实原理和 ObjectProvider 延迟注入的原理都差不多。

下面看一下 QualifierAnnotationAutowireCandidateResolver 是如何处理 @Qualifier 和 @Value 注解的。

2. QualifierAnnotationAutowireCandidateResolver

QualifierAnnotationAutowireCandidateResolver 需要与 AutowiredAnnotationBeanPostProcessor 配合使用,处理了 @Value 、@Autowire 、@Qualifier 三个注解。

  1. @Value:getSuggestedValue 方法用于读取依赖注入值。
  2. @Autowire:isRequired 方法用于判断是否是必须依赖的值。
  3. @Qualifier:isAutowireCandidate 添加过滤规则用于精确判断 bean 是否可以注入。将 "候选 Bean 元信息" 和 "注入点 @Qualifier 注解" 中的属性进行匹配,如果匹配成功就可以注入,默认情况下匹配 bean 的名称。

2.1 @Value 处理

在 Spring 中可以使用 @Value 注入配置属性

@Value("${jdbd.url}")
private String url;

@Value 的处理方式如下,查找到 @Value 的值作为 AutowireCandidateResolver#getSuggestedValue 的返回值。

private Class<? extends Annotation> valueAnnotationType = Value.class;

@Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
// 1. 先查找字段或方法参数上的注解
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
// 2. 查找方法上的注解
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}

说明: getSuggestedValue 方法先处理注入点(包括字段或方法参数上)的注解信息,如果没有再查找方法上的注解。如下,@Value 和 @Qualifier 两个注解,方法参数上的注解都优先于方法上的注解。

@Value("${name1}")
public void setName(@Value("${name2}") String name) {
...
}

2.2 @Autowire 处理

@Override
public boolean isRequired(DependencyDescriptor descriptor) {
if (!super.isRequired(descriptor)) {
return false;
}
Autowired autowired = descriptor.getAnnotation(Autowired.class);
return (autowired == null || autowired.required());
}

说明: @Autowired 默认 require=true。

2.3 @Qualifier 处理

@Autowire
@Qualifier("user")
private User user;

3. @Qualifier 源码分析

在使用 Spring 框架中进行自动注人时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常。Spring 允许我们通过 Qualifier 指定注入 Bean 的名称,这样歧义就消除了。@Qualifier 四种用法介绍

3.1 <qualifier> 标签解析

(1)普通 Bean 定义:不带任何 <qualifier> 标签(默认)

<bean id="user1" class="com.binarylei.spring.ioc.domain.User">

匹配时默认匹配 User Bean 的名称。即:

@Qualifier("user1")
private User user;

(2)Bean 定义:配置 <qualifier> 标签

<bean id="user2" class="com.binarylei.spring.ioc.domain.User">
<qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="xxxx">
<attribute key="name" value="binarylei"/>
<attribute key="age" value="20"/>
</qualifier>
<qualifier type="org.springframework.beans.factory.annotation.Qualifier2" value="xxxx">
<attribute key="name" value="binarylei"/>
</qualifier>
</bean>

说明: <qualifier> 标签可以有多个,如果 type 类型相同,后面的会覆盖前面的。每个 <qualifier> 最终会解析成 AutowireCandidateQualifier,然后添加 AbstractBeanDefinition 中 Map<String, AutowireCandidateQualifier> qualifiers;

private final String typeName;

private final Map<String, Object> attributes = new LinkedHashMap<>();
public static final String VALUE_KEY = "value";
  1. type 属性:默认为 org.springframework.beans.factory.annotation.Qualifier。
  2. value 属性:将 key = "value" 的属性添加到 attributes。
  3. <attribute> 标签:其它属性值,同样添加到 attributes。
  4. 每个 AutowireCandidateQualifier 会添加到 bd.qualifiers 中。

思考:<qualifier> 标签能定义这么多的属性,但 @Qualifier 标签只有一个 value 属性,如何命中标签中的其它属性呢?

其实这就涉及到 @Qualifier 自定义注解,在自定义注解中,我们可以添加属性,这样就可以和标签中的属性进行匹配了。同时如果是注解驱动,如果要定义多个属性,也需要使用自定义注解。但多个属性的情况,我们好像很少用到。

Spring Cloud @LoadBalanced 注解,就是对 @Qualifier 的简单扩展,支持分组注入。

@Qualifier
public @interface LoadBalanced {
}

(3)Bean 定义:注解驱动

@Bean
@Qualifier(value = "uri1")
private URI uri1() {
return URI.create("http://www.baidu.com");
}

注意: 注解驱动不会将 @Qualifier 解析到 bd.qualifiers 中,会直接读取注入点的 @Qualifier 注解,使用 SynthesizedMergedAnnotationInvocationHandler#annotationEquals 直接比较两个注解的属性是否全部相等。

(4)Bean 定义:自定义注解,分组注入

@Bean
@LoadBalanced
private URI uri1() {
return URI.create("http://www.baidu.com");
} @Qualifier("user1")
private URI uri;

说明: @Qualifier 注解有一个特性,如果在注入的字段上加上 @Qualifier 注解,则会将定义 Bean 时标注了 @Qualifier 的对象注入进来。其派生注解 @LoadBalanced 也有同样的特性。当然 @Qualifier 的这种特性本质上一种过滤规则,后面分析源码时会分析。

3.2 默认处理 Spring 和 JDK 的 @Qualifier 注解

  • @org.springframework.beans.factory.annotation.Qualifier
  • @javax.inject.Qualifier
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes.add(Qualifier.class);
// JSR-330
this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
}

在分析 @Qualifier 源码之前,我们先看一下这几个方法的功能:

  • isAutowireCandidate:先校验字段或方法上的 @Qualifier 注解,如果没有则回退到方法上的 @Qualifier 注解 。如果有 @Qualifier ,则需要对候选对象进行过滤。
  • checkQualifiers:根据 @Qualifier 注解对候选对象进行过滤。该方法支持 @Qualifier 派生注解,也就是自定义 @Qualifier 注解。
  • checkQualifier:实际进行二重匹配:①注解类型匹配 ②注解属性匹配。如果元信息没有定义,即 bd.qualifiers=null,此时 @Qualifier 直接匹配 beanName 即可。
    • 注解驱动:直接匹配比较 "Bean 元信息 @Qualifier 注解" 和 "注入点 @Qualifier 注解" 是否相等,即比较两个注解的属性是否相等。
    • 传统方式:比较 bd.qualifiers 和 "注入点 @Qualifier 注解" 属性是否相等。比较规则如下:bd.qualifier.attributeName -> bd.attributeName -> beanName -> @Qualifier.defaultValue。

3.2 isAutowireCandidate

isAutowireCandidate 用于精确判断 bean 是否可以注入。将 "候选 Bean 元信息" 和 "注入点 @Qualifier 注解" 中的属性进行匹配,如果匹配成功就可以注入,默认情况下匹配 bean 的名称。

  1. 同 @Value 注解一样,@Qualifier 注解也是注入点(字段、方法参数)的注解都优先于方法上的注解。

  2. 将 "注入点 @Qualifier 注解" 的属性和 bd.qualifier 进行匹配。匹配分两步:

    • 首先,注解类型匹配。bd.qualifier 也可以自定义 @Qualifier 注解,所以需要先匹配注解类型。

    • 其次,属性值匹配。将 "注入点 @Qualifier 注解" 和 bd.qualifier 属性值一一比较。

      默认情况下,@Qualifier 只有一个健为 value 的属性键值对,也只能与 bd.qualifier.value 进行匹配,如果没有定义健 value,则默认使用 beanName 进行匹配。

      当然,我们可以自定义 @Qualifier 注解,也就会有多个属性键值对。

@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
if (match) {
// 1. 先查找字段或方法参数上的注解
match = checkQualifiers(bdHolder, descriptor.getAnnotations());
if (match) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class == method.getReturnType()) {
// 2. 查找方法上的注解
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
}
}
}
}
return match;
}

说明: 同 @Value 注解,@Qualifier 注解也是方法参数上的注解都优先于方法上的注解。最核心的方法是 checkQualifiers(bdHolder, descriptor.getAnnotations()),用于校验所有的 @Qualifier 是否匹配成功。

3.3 checkQualifiers

checkQualifiers 将从注入点提取出的注解和 bd.qualifiers 进行匹配,并且该方法的一个显著特征是支持 @Qualifier 派生注解,也就是自定义 @Qualifier 注解

  1. 判断是否是 @Qualifier 注解。这时的 @Qualifier 注解是一个宽泛的概念,即包括前文中 qualifierTypes 定义的注解,也包括自定义的派生注解,也就是元注解(@Qualifier 的元注解只支持一层)。
  2. @Qualifier 注解和 bd.qualifier 进行匹配。匹配成功,直接返回 true。否则进行元注解匹配。
  3. 元注解匹配。checkQualifiers 只匹配第一层元注解,不会嵌套的循环解析。
  4. 如果没有任何 @Qualifier 注解,则返回 true。这也是合理的,@Qualifier 本质是一种过滤规则,没有配置过滤规则,当然要返回 true。
protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
if (ObjectUtils.isEmpty(annotationsToSearch)) {
return true;
}
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
for (Annotation annotation : annotationsToSearch) {
Class<? extends Annotation> type = annotation.annotationType();
boolean checkMeta = true;
boolean fallbackToMeta = false;
// 1. 如果本身是@Qualifier注解,且匹配成功,则不需要解析元注解
if (isQualifier(type)) {
if (!checkQualifier(bdHolder, annotation, typeConverter)) {
fallbackToMeta = true;
} else {
checkMeta = false;
}
}
// 2. 两种情况需要解析元注解:一是本身不是@Qualifier注解,二是匹配失败
if (checkMeta) {
boolean foundMeta = false;
// 2.1 遍历元注解,即注解上的注解。只遍历一层元注解。
for (Annotation metaAnn : type.getAnnotations()) {
Class<? extends Annotation> metaType = metaAnn.annotationType();
// 2.2 元注解是@Qualifier注解
if (isQualifier(metaType)) {
foundMeta = true;
// 1. fallbackToMeta=true说明第一次匹配失败,此时元注解必须定义value值?
// 2. 元注解匹配失败
if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
!checkQualifier(bdHolder, metaAnn, typeConverter)) {
return false;
}
}
}
// 2.3 两次匹配失败,才算匹配失败
// fallbackToMeta=true表示第一次匹配失败,如果第一次匹配成功就直接返回true了
// foundMeta=true表示第二次匹配成功,因为如果匹配失败,则已经返回false
if (fallbackToMeta && !foundMeta) {
return false;
}
}
}
return true;
}

说明: checkQualifiers 对注入点上的 @Qualifier(包派生注解) 注解逐一匹配,只要有匹配失败的返回 false。checkQualifiers 最显要的特性是支持 @Qualifier 派生注解,匹配的规则看起来就比较复杂。大部分场景(包括自定义注解),只需要进行第一次匹配即可。

下面介绍一下 @Qualifier 派生注解。因为注解是没有继承或实现一说了,为了复用注解,Spring 提出了派生注解的概念。在本例中,@MyQualifier1 是 @Qualifier 派生而来,@MyQualifier2 是 @MyQualifier1 派生而来。

@Qualifier
private static @interface MyQualifier1 {
} @MyQualifier1
private static @interface MyQualifier2 {
} @MyQualifier1
@Qualifier
private static @interface MyQualifier3 {
}

isQualifier 方法,已经考虑了自定义的派生注解情况,也就是 @MyQualifier1 会直接匹配,只有 @MyQualifier2 则是元注解匹配。@MyQualifier3 则可能先直接匹配再元注解匹配。如此可见,大部分场景,我们也用不到二次匹配,也不会定义这么复杂的注解。

protected boolean isQualifier(Class<? extends Annotation> annotationType) {
for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
return true;
}
}
return false;
}

3.4 checkQualifier

最后一步是 checkQualifier(bdHolder, annotation, typeConverter) 将 bd.qualifiers 的信息和当前 @Qualifier 注解的信息进行匹配。大多数情况下 bd.qualifiers 为空,@Qualifier 直接匹配 beanName 即可:

  1. 首先,要了解 XML 和注解驱动时 bd 的解析不同。bd.qualifiers 一般只有在 XML 配置中才会解析 <qualifier> 标签,而 Spring 注解驱动则不会解析 @Qualifier 注解元信息。所以 bd.qualifiers=null 有两种情况:一是没有配置 <qualifier> 标签;二是使用 Spring 注解驱动。
  2. 注解驱动配置:直接匹配 "Bean 元信息 @Qualifier 注解" 和 "注入点 @Qualifier 注解" 是否相等,即比较两个注解的属性是否相等。Bean 元信息 @Qualifier 注解有两种获取方式:一是获取 Bean 定义上的注解;二是获取 Bean 对象类型上的注解。注解比较涉及到 Spring 注解解析的内容,详见方法 SynthesizedMergedAnnotationInvocationHandler#annotationEquals。
  3. 常量 XML 配置:比较 bd.qualifiers 和 "注入点 @Qualifier 注解" 属性是否相等。比较规则如下:bd.qualifier.attributeName -> bd.attributeName -> beanName -> @Qualifier.defaultValue。
protected boolean checkQualifier(
BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) { Class<? extends Annotation> type = annotation.annotationType();
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition(); // 1. 获取 BeanDefinition 中的 bd.qualifier
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
if (qualifier == null) {
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
// 2. 注解驱动配置:bd.qualifier=null 一般为注解驱动,targetAnnotation.equals(annotation)
if (qualifier == null) {
// 2.1 db.qualifiedElement 一般没有赋值,不会使用
Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type)
// 2.2 db.factoryMethodToIntrospect 获取工厂方法上的注解,@Bean配置方式,主要获取方式
if (targetAnnotation == null) {
targetAnnotation = getFactoryMethodAnnotation(bd, type);
}
// 2.3 db.decoratedDefinition
if (targetAnnotation == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
if (dbd != null) {
targetAnnotation = getFactoryMethodAnnotation(dbd, type);
}
}
// 2.4 尝试在对象类型上获取@Qualifier,之前的方式都是在Bean定义的位置获取
if (targetAnnotation == null) {
if (getBeanFactory() != null) {
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
}
}
if (targetAnnotation == null && bd.hasBeanClass()) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);
}
}
// 2.5 将”Bean元信息注解"和"注入点注解"属性进行比较
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
return true;
}
} // 2. 常量 XML 配置:bd.qualifier 此时不为空,除非没有配置<qualifier>标签
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
// 2.1 attributes.isEmpty()为空肯定是自定义注解,否则@Qualifier至少有value=""的属性
// 此时有自定义注解,却bd.qualifier=null,肯定无法匹配
if (attributes.isEmpty() && qualifier == null) {
return false;
}
// 2.2 注解attributes和bd.qualifier属性值进行匹配
// db.qualifier.attributeName -> db.attributeName ->
// beanName -> @Qualifier.defaultValue
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
String attributeName = entry.getKey();
Object expectedValue = entry.getValue();
Object actualValue = null;
if (qualifier != null) {
actualValue = qualifier.getAttribute(attributeName);
}
if (actualValue == null) {
actualValue = bd.getAttribute(attributeName);
}
// 默认和beanName进行比较
if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
continue;
}
if (actualValue == null && qualifier != null) {
actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
}
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
if (!expectedValue.equals(actualValue)) {
return false;
}
}
return true;
}

说明: 总结一下,checkQualifier 方法整体而言,分了两种场景:一是注解驱动;二是 XML 配置。通常bd.qualifiers=null 表示注解驱动,因为注解驱动解析时不会将 @Qualifier 添加到 bd.qualifiers 中。当然 bd.qualifiers=null 还有一种可能是没有配置 <qualifier> 标签,此时只需要比较 beanNama 名称即可。

  1. 注解驱动:最主要的任务是如何获取 "Bean 元信息 @Qualifier 注解",Spring 提供了两种获取其注解的方法

    • Bean 定义上获取注解:db.qualifiedElement -> db.factoryMethodToIntrospect -> db.decoratedDefinition 都是尝试获取其定义的注解。其中最主要的获取方式是工厂方式 db.factoryMethodToIntrospect。
    • Bean 对象类型上获取注解:beanFactory#getType -> db.beanClass 尝试获取对象类型上的注解。

    最后直接比较 "Bean 元信息 @Qualifier 注解" 和 "注入点 @Qualifier 注解" 这两个注解是否相等,即比较两个注解的属性是否相等。

  2. XML 配置:传统的方式。我们使用的大多数场景是没有配置 bd.qualifier 属性的,这时直接比较 beanName 即可。将注解中的属性 attributeName 和配置 db 进行比较,比较规则如下:db.qualifier.attributeName -> db.attributeName -> beanName -> @Qualifier.defaultValue。

4. GenericTypeAwareAutowireCandidateResolver

GenericTypeAwareAutowireCandidateResolver 检查依赖的类型 dependencyType 和实际注入的类型 targetType 上的泛型是否匹配。isAutowireCandidate 方法调用 checkGenericTypeMatch 判断泛型是否匹配。其中 ResolvableType 是 Spring 提供的专门处理泛型的 API。

checkGenericTypeMatch 方法最关键是获取实际注入类型 targetType 的泛型:

  • 依赖类型 dependencyType:直接读取 descriptor.resolvableType。

  • 实际注入类型 targetType:获取比较复杂,主要原因是 Bean 的创建方式多样。

    • 工厂方法创建:如 @Bean 等都是工厂方法创建,从方法返回值获取其泛型类型,即 bd.factoryMethodToIntrospect -> db.decoratedDefinition.factoryMethodToIntrospect。当然如果其工厂的返回值类型已经解析就直接返回,AbstractAutowireCapableBeanFactory#getTypeForFactoryMethod 类型自省时会缓存其类型 bd.factoryMethodReturnType。
    • 非工厂方法创建:beanFactory#getType -> bd.beanClass。
protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
// 1. 依赖类型 dependencyType
ResolvableType dependencyType = descriptor.getResolvableType();
// 2. 依赖类型没有泛型,直接返回。因为既然调用该方法,那就是根据类型查找依赖,class已经匹配过
if (dependencyType.getType() instanceof Class) {
return true;
} ResolvableType targetType = null;
boolean cacheType = false;
RootBeanDefinition rbd = null;
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
}
// 3. 工厂方法创建,查找实际注入的类型,如 @Bean
// 注意:工厂方法解析的类型和依赖类型dependencyType不匹配时,返回null
if (rbd != null) {
targetType = rbd.targetType;
if (targetType == null) {
cacheType = true;
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
if (targetType == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
if (dbd != null) {
targetType = dbd.targetType;
if (targetType == null) {
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
}
}
}
}
} // 4. 非工厂方法创建,beanFactor#getType -> bd.beanClass
if (targetType == null) {
// 4.1 getType 会直接获取实例的类型,再读取bd信息
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanType));
}
}
// 4.2 Fallback: no BeanFactory set, or no type resolvable。读取 bd.beanClass
if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
Class<?> beanClass = rbd.getBeanClass();
if (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
}
}
} // 5. 结果配置
// 5.1 targetType==null
if (targetType == null) {
return true;
}
if (cacheType) {
rbd.targetType = targetType;
}
// 5.2 descriptor.fallbackMatchAllowed表示精确匹配时匹配失败,回退至泛型无法解析
if (descriptor.fallbackMatchAllowed() &&
(targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
return true;
}
// 5.3 泛型直接匹配
return dependencyType.isAssignableFrom(targetType);
}

说明: checkGenericTypeMatch 匹配泛型过程非常复杂,最重要的原因还是 Bean 的创建方式有多种,导致想获取实际注入的类型 targetType 也非常复杂。但我们也无需了解所有的场景,只需要知道大致可以分为两种场景:一类是工厂方法创建,大致对应注解驱动,因为 @Bean 实际上也是通过工厂方法创建的,另一类是非工厂方法创建,可以认为是传统方式创建的,直接从 beanFactory#getType 获取其类型。

  1. 获取依赖类型 dependencyType。

  2. 如果 dependencyType 不包含泛型,直接返回 true。想要知道为什么没有泛型就不用匹配 Class 类型?

    原因要从 checkGenericTypeMatch 使用场景说起,根据名称查找依赖是精确查找,不需要对候选对象进行过滤。只有根据类型进行依赖查找才会使用该方法,因为类型查找是模糊查找,可能结果有多个,需要对候选对象过滤,从而调用 checkGenericTypeMatch 方法,而过滤的对象已经按 Class 类型进行类型匹配过了。

    大致的调用链路如下:beanFactory#resolveDependency -> beanFactory#findAutowireCandidates -> beanFactory#isAutowireCandidate -> resolver#isAutowireCandidate -> resolver#checkGenericTypeMatch。

  3. 工厂方法创建方式获取实际注入类型 targetType:使用场景主要是注解驱动,因为 @Bean 本身是通过工厂方式创建。beanFactory#getTypeForFactoryMethod 内省自省时会缓存 bd.factoryMethodReturnType。

  4. 非工厂方法创建方式获取实际注入类型 targetType:传统的获取方式。

    • beanFactory#getType:先从实例上获取实际类型,如果没有实例化,再从 bd 中获取其实际类型。
    • bd.beanClass:如果 beanFactory 没有设置,那就只能从 bd 中获取其类型了。
  5. 泛型匹配:依赖类型 dependencyType 和实际注入的类型 targetType 进行匹配

    • 没有获取 targetType:直接返回 false。
    • targetType 包含有无法创建的泛型:只有泛型精确匹配失败,才会进行匹配。
    • 泛型精确匹配。直接调用 ResolvableType API。

5. ContextAnnotationAutowireCandidateResolver

ContextAnnotationAutowireCandidateResolver 用来处理 @Lazy 延迟注入的问题。其核心方法是 getLazyResolutionProxyIfNecessary:

  • 首先,判断注入点是否有 @Lazy 注解。和 @Value 、@Autowire 、@Qualifier 一样,也是先查找注入点(字段,参数),再查找方法上。
  • 生成代理对象。只有使用到该对象时才会真正调用 beanFactory#doResolveDependency 查找依赖,其实和 ObjectProvider 延迟注入的原理都差不多。

5.1 @Lazy 处理

@Autowired
@Lazy
private Environment environmentLazy; // 实际注入一个代理对象

5.2 源码分析

@Override
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}

说明: isLazy 方法和 @Value 、@Autowire 、@Qualifier 处理都差不多,我们看一下 buildLazyResolutionProxy 方法,其实也很简单,无非是生成一个代理对象。核心就一句代码 beanFactory.doResolveDependency。

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
if (target == null) {
Class<?> type = getTargetClass();
if (Map.class == type) {
return Collections.emptyMap();
}
else if (List.class == type) {
return Collections.emptyList();
}
else if (Set.class == type || Collection.class == type) {
return Collections.emptySet();
}
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
"Optional dependency not present for lazy injection point");
}
return target;
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
return pf.getProxy(beanFactory.getBeanClassLoader());
}

说明: ProxyFactory 是 spring-aop 中生成代理对象的工具类,不在本文的讨论范围内。如果 dependencyType 是接口,使用 JDK 动态代理,否则使用 CGLIB 代理。


每天用心记录一点点。内容也许不重要,但习惯很重要!

上一篇:jmeter 单接口测试方案(接口无业务关联)


下一篇:npm ERR! mathine_call@1.0.0 dev: `webpack-dev-server --inline --progress --config build/webpack.dev.conf.js` npm ERR! Exit status 1