• Spring IoC 依赖注入(三)resolveDependency


    Spring IoC 依赖注入(三)resolveDependency

    resolveDependency 是 Spring 进行依赖查找的核心 API。弄明白了 resolveDependency,基本上依赖注入的问题也就搞明白了一半。resolveDependency 本质是根据类型查找依赖,调用 beanFactory#beanNamesForType 方法根据类型查找依赖名称。

    1. 根据名称查找依赖:getBean(beanName)。
    2. 根据类型查找依赖:beanFactory#resolveDependency,本文会深入分析依赖查找的源码。

    认知一下,与依赖查找的相关 API:

    1. resolveDependency:支持 Optional、延迟注入、懒加载注入、正常注入。

    2. doResolveDependency:在依赖查找之前,想办法快速查找,如缓存 beanName、@Value 等直接获取注入的值,避免通过类型查找,最后才对集合依赖和单一依赖分别进行了处理。实际上,无论是集合依赖还是单一依赖查找都是调用 findAutowireCandidates 方法。

    3. findAutowireCandidates:真正在 Spring IoC 容器中进行依赖查找,依赖查找的来源有三:①内部对象 ②托管Bean ③BeanDefinition。最后如果无法查找到依赖对象,会进行一些补偿机制,想方设法获取注入的对象,如泛型补偿,自引用补偿。

    4. isAutowireCandidate:判断候选对象是否可用,有三重过滤规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。委托给 ContextAnnotationAutowireCandidateResolver。

      isAutowireCandidate 方法过滤候选对象

    首先,我们也先认知一下这几个类:

    1. ParameterNameDiscoverer:用于提取方法参数名称。
    2. DependencyDescriptor:封装了依赖注入点的详细信息,可以是字段 Field,也可以是方法参数 MethodParameter。
    3. AutowireCandidateResolver:判断 DependencyDescriptor 是否是可注入对象。Spring IoC 容器默认实现为 ContextAnnotationAutowireCandidateResolver。

    1. resolveDependency

    resolveDependency 依赖查找解决了以下场景:

    1. Optional:JDK8 提供了 API。主要是将依赖设置非强制依赖,即 descriptor.required=false。
    2. 延迟依赖注入支持:ObjectFactory、ObjectProvider、javax.inject.Provider 没有本质的区别。
    3. 另一种延迟注入的支持 - @Lazy 属性。
    4. 根据类型查找依赖 - doResolveDependency。
    @Override
    public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
        // ParameterNameDiscovery用于解析方法参数名称
        descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
        // 1. Optional<T>
        if (Optional.class == descriptor.getDependencyType()) {
            return createOptionalDependency(descriptor, requestingBeanName);
        // 2. ObjectFactory<T>、ObjectProvider<T>
        } else if (ObjectFactory.class == descriptor.getDependencyType() ||
                 ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        // 3. javax.inject.Provider<T>
        } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
        } else {
            // 4. @Lazy
            Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                descriptor, requestingBeanName);
            // 5. 正常情况
            if (result == null) {
                result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
    }
    

    说明: 前四种场景(Optional,延迟注入 ObjectProvider + @Lazy),我们先放一下,重点分析一下最基本的使用场景,Spring 是如何进行依赖查找的 - doResolveDependency。其实无论是什么场景,最底层都是调用 doResolveDependency。

    2. doResolveDependency

    doResolveDependency 封装了依赖查找的各种情况:

    1. 快速查找: @Autowired 注解处理场景。AutowiredAnnotationBeanPostProcessor 处理 @Autowired 注解时,如果注入的对象只有一个,会将该 bean 对应的名称缓存起来,下次直接通过名称查找会快很多。
    2. 注入指定值:@Value 注解处理场景。QualifierAnnotationAutowireCandidateResolver 处理 @Value 注解时,会读取 @Value 对应的值进行注入。如果是 String 要经过三个过程:①占位符处理 -> ②EL 表达式解析 -> ③类型转换,这也是一般的处理过程,BeanDefinitionValueResolver 处理 String 对象也是这个过程。
    3. 集合依赖查询:直接全部委托给 resolveMultipleBeans 方法。
    4. 单个依赖查询:先调用 findAutowireCandidates 查找所有可用的依赖,如果有多个依赖,则根据规则匹配: @Primary -> @Priority -> ③方法名称或字段名称。
    public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    
        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            // 1. 快速查找,根据名称查找。AutowiredAnnotationBeanPostProcessor用到
            Object shortcut = descriptor.resolveShortcut(this);
            if (shortcut != null) {
                return shortcut;
            }
    
            // 2. 注入指定值,QualifierAnnotationAutowireCandidateResolver解析@Value会用到
            Class<?> type = descriptor.getDependencyType();
            Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
            if (value != null) {
                if (value instanceof String) {
                    // 2.1 占位符解析
                    String strVal = resolveEmbeddedValue((String) value);
                    BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                                         getMergedBeanDefinition(beanName) : null);
                    // 2.2 Spring EL 表达式
                    value = evaluateBeanDefinitionString(strVal, bd);
                }
                TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
                try {
                    // 2.3 类型转换
                    return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
                } catch (UnsupportedOperationException ex) {
                    return (descriptor.getField() != null ?
                            converter.convertIfNecessary(value, type, descriptor.getField()) :
                            converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
                }
            }
    
            // 3. 集合依赖,如 Array、List、Set、Map。内部查找依赖也是使用findAutowireCandidates
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }
    
            // 4. 单个依赖查询
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
            // 4.1 没有查找到依赖,判断descriptor.require
            if (matchingBeans.isEmpty()) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                return null;
            }
    
            String autowiredBeanName;
            Object instanceCandidate;
    
            // 4.2 有多个,如何过滤
            if (matchingBeans.size() > 1) {
                // 4.2.1 @Primary -> @Priority -> 方法名称或字段名称匹配 
                autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                // 4.2.2 根据是否必须,抛出异常。注意这里如果是集合处理,则返回null
                if (autowiredBeanName == null) {
                    if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                        return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                    } else {
                        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();
            }
    
            // 4.3 到了这,说明有且仅有命中一个
            if (autowiredBeanNames != null) {
                autowiredBeanNames.add(autowiredBeanName);
            }
            // 4.4 实际上调用 getBean(autowiredBeanName, type)。但什么情况下会出现这种场景?
            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);
        }
    }
    

    说明: doResolveDependency 方法的四个功能,快速查找和集合处理都委托给了其它方法,注入指定值虽然看起来复杂,但占位符处理、EL 表达式解析、类型转换这三个功能点都有具体的类处理,也不是本文的重点。

    我们重点看一下单个依赖的查询,弄明白了单个依赖的查询,其它集合依赖也差不多。

    1. 查找容器中所有可用依赖:findAutowireCandidates 方法根据类型查找依赖。
    2. 如何有多个依赖怎么处理?其实 Spring 有一套通用的流程,先按 @Primary 查找,再按 @Priority,最后按方法名称或字段名称查找,直到只有一个 bean 为止。相关的匹配规则见 determineAutowireCandidate 方法。
    3. 此时只有一个依赖,从容器获取真实的 bean。descriptor.resolveCandidate 方法根据名称 autowiredBeanName 实例化对象。

    思考:findAutowireCandidates 返回的为什么是对象类型,而不是实例对象?

    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    

    matchingBeans 中的 Object 对象可能是对象类型,而不全部是实例对象。因为 findAutowireCandidates 方法是根据类型 type 查找名称 beanNames,如果容器中该 beanName 还没有实例化,findAutowireCandidates 不会画蛇添足直接实例化该 bean,当然如果已经实例化了会直接返回这个 bean。

    3. findAutowireCandidates

    根据上面的分析,resolveDependency 方法对 Optional、延迟注入、懒加载注入等分别进行了处理。之后 doResolveDependency 在正式查找之前看能不能快速查找,如缓存 beanName、@Value 等快速指定需要注入的值,避免通过类型查找,最后才对集合依赖和单一依赖分别进行了处理。实际上,无论是集合依赖还是单一依赖查找,本质上都是调用 findAutowireCandidates 进行类型依赖查找。

    从 findAutowireCandidates 方法,我们可以看到 Spring IoC 依赖注入的来源:

    1. 先查找 Spring IoC 内部依赖 resolvableDependencies。在 AbstractApplicationContext#prepareBeanFactory 方法中默认设置了如下内部依赖:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。
    2. 在父子容器进行类型查找:查找类型匹配的 beanNames,beanFactory#beanNamesForType 方法根据类型查找是,先匹配单例实例类型(包括 Spring 托管 Bean),再匹配 BeanDefinition 的类型。从这一步,我们可以看到 Spring 依赖注入的另外两个来源:一是 Spring 托管的外部 Bean,二是 Spring BeanDefinition。
    protected Map<String, Object> findAutowireCandidates(
        @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
        
        Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
        // 1. Spring IoC 内部依赖 resolvableDependencies
        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;
                }
            }
        }
        
        // 2. 类型查找:本质上递归调用beanFactory#beanNamesForType。先匹配实例类型,再匹配bd。
        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this, requiredType, true, descriptor.isEager());
        for (String candidate : candidateNames) {
            // 2.1 isSelfReference说明beanName和candidate本质是同一个对象
            //     isAutowireCandidate进一步匹配bd.autowireCandidate、泛型、@@Qualifier等进行过滤
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                // 2.2 添加到候选对象中
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        
        // 3. 补偿机制:如果依赖查找无法匹配,怎么办?包含泛型补偿和自身引用补偿两种。
        if (result.isEmpty()) {
            boolean multiple = indicatesMultipleBeans(requiredType);
            // 3.1 fallbackDescriptor: 泛型补偿,实际上是允许注入对象类型的泛型存在无法解析的情况
            DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
            // 3.2 补偿1:不允许自称依赖,但如果是集合依赖,需要过滤非@Qualifier对象。什么场景?
            for (String candidate : candidateNames) {
                if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
                    (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
                    addCandidateEntry(result, candidate, descriptor, requiredType);
                }
            }
            // 3.3 补偿2:允许自称依赖,但如果是集合依赖,注入的集合依赖中需要过滤自己
            if (result.isEmpty() && !multiple) {
                for (String candidate : candidateNames) {
                    if (isSelfReference(beanName, candidate) &&
                        (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
                        isAutowireCandidate(candidate, fallbackDescriptor)) {
                        addCandidateEntry(result, candidate, descriptor, requiredType);
                    }
                }
            }
        }
        return result;
    }
    

    说明: findAutowireCandidates 大致可以分为三步:先查找内部依赖,再根据类型查找,最后没有可注入的依赖则进行补偿。

    1. 查找内部依赖:Spring IoC 容器本身相关依赖,这部分内容是用户而言是透明的,也不用感知。resolvableDependencies 集合中注册如 BeanFactory、ApplicationContext 、ResourceLoader、ApplicationEventPublisher 等。
    2. 根据类型查找:包括 ①外部托管 Bean ②注册 BeanDefinition。类型查找调用 beanFactory#beanNamesForType 方法,详见 Spring IoC 依赖查找之类型自省。我们来看一下如何过滤的。
      • 自身引用:isSelfReference 方法判断 beanName 和 candidate 是否是同一个对象,包括两种情况:一是名称完全相同,二是 candidate 对应的工厂对象创建了 beanName。
      • 是否可以注入:底层实际调用 resolver.isAutowireCandidate 方法进行过滤,包含三重规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。下面会详细介绍这个方法。
    3. 补偿机制:如果依赖查找无法匹配,怎么办?Spring 提供了两种补偿机制:一是泛型补偿,允许注入对象对象的泛型无法解析,二是自身引用补偿,对这两种机制使用如下:
      • 先使用泛型补偿,不允许自身引用:即 fallbackDescriptor。此时如果是集合依赖,对象必须是 @Qualifier 类型。
      • 允许泛型补偿和自身引用补偿:但如果是集合依赖,必须过滤自己本身,即 beanName.equals(candidate) 必须剔除。

    现在 findAutowireCandidates 处理过程,基本上很清晰了,还有两个小问题需要再澄清一下:

    1. isAutowireCandidate 方法是如何过滤修改对象?
    2. addCandidateEntry 最终最终返回的都是实例对象吗?

    先看一下 addCandidateEntry 方法,如果对象还未实例化,Spring 不会画蛇添足将 candidateName 通过 getName 提前实例化。之所以要强调这点,是因为 Spring 的 Bean 生命周期,其实从 Bean 还未实例化就已经开始,Spring 会尽可能的不要初始化该 Bean,除非显式调用 getBean 或不得不实例化时,这点在阅读源码是会感受非常强烈,我们在使用 Spring API 时也要非常注意这点。

    private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
                                   DependencyDescriptor descriptor, Class<?> requiredType) {
    
        // 1. 集合依赖,直接调用 getName(candidateName) 实例化
        if (descriptor instanceof MultiElementDescriptor) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            if (!(beanInstance instanceof NullBean)) {
                candidates.put(candidateName, beanInstance);
            }
        // 2. 已经实例化,直接返回实例对象
        } else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor && ((StreamDependencyDescriptor) descriptor).isOrdered())) {
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
        // 3. 只获取candidateName的类型,真正需要注入时才实例化对象
        } else {
            candidates.put(candidateName, getType(candidateName));
        }
    }
    

    说明: descriptor.resolveCandidate 基本上都是直接调用 getName(beanName) 实例化 bean。在大部分场景中,addCandidateEntry 方法只会以返回该 candidateName 对应的类型,而不会提前实例该对象。

    4. isAutowireCandidate

    isAutowireCandidate 判断候选对象是否可用。实际是都是委托给 AutowireCandidateResolver#isAutowireCandidate 接口判断,Spring 中默认的实现是 ContextAnnotationAutowireCandidateResolver。

    isAutowireCandidate 方法过滤候选对象有三重规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。更多源码分析见 Spring 注解原理 AutowireCandidateResolver:@Qualifier @Value

    protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
            DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {
        String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
        // 1. 传统方式:解析 bd.beanClass,注意 Spring注解驱动时根本不会配置beanClassName
        resolveBeanClass(mbd, beanDefinitionName);
        // 2. 注解驱动:解析工厂方法 bd.factoryMethodToIntrospect
        if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) {
            new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
        }
        // 3. 直接委托给AutowireCandidateResolver
        return resolver.isAutowireCandidate(
            new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor);
    }
    

    说明: 主要注意一下传统方式和注解驱动获取 Bean 类型的不同:

    • 传统方式:配置 beanClassName,直接解析成 beanClass,从而获取对象类型。
    • 注解驱动:如 @Bean 方式,需要解析方法返回值类型,获取对象类型。

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

  • 相关阅读:
    第三次作业
    C语言第二次作业
    第一次作业
    C语言第六次作业
    C语言第五次作业
    C语言第四次作业
    C语言第三次作业
    C语言第二次作业
    C语言第一次作业
    C语言学习笔记(6)
  • 原文地址:https://www.cnblogs.com/binarylei/p/12337145.html
Copyright © 2020-2023  润新知