zoukankan      html  css  js  c++  java
  • 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 代理。


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

  • 相关阅读:
    stream流的统计demo
    ResourceBundle 读取文件demo
    spring boot 配置Filter过滤器的两种方式
    java工厂模式demo
    ThreadLocalDemo
    观察者模式Demo
    大数字的计算
    rabbitMQ消息丢失
    CF671E(线段树+单调栈)
    2020集训队作业板刷记录(三)
  • 原文地址:https://www.cnblogs.com/binarylei/p/10428999.html
Copyright © 2011-2022 走看看