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

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 的实现有以下几个:

  • 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 代理。


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

posted on 2019-02-25 08:18  binarylei  阅读(2481)  评论(0编辑  收藏  举报

导航