zoukankan      html  css  js  c++  java
  • Spring 注解原理(二)AutowiredAnnotationBeanPostProcessor:@Autowired @Value @Inject @Lookup

    Spring 注解原理(二)AutowiredAnnotationBeanPostProcessor:@Autowired @Value @Inject @Lookup

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

    AutowiredAnnotationBeanPostProcessor 是 Spring 注解驱动的核心组件之一,都是处理的 Bean 的依赖注入,相关的注解有 @Autowired @Value @Inject @Lookup 四个,也可以自定义注解后添到 autowiredAnnotationTypes 集合中。

    • @Autowired @Value @Inject:这三个注解的逻辑完全一样,都是处理依赖注入,其优先级 @Autowired > @Value > @Inject。因此本文在此做如下约定,@Autowired 一般指的是这三个注解。
    • @Lookup:本质也是解决依赖注入的问题,但和上面三个注解不同的是,@Lookup 注入的对象是动态的( 尤其是 prototype 实例),而 @Autowired 注入的对象是静态的,一旦注入就不可发生改变。@Lookup 只能标注在抽象方法上,实例化时使用 CglibSubclassingInstantiationStrategy 进行字节码提升,每次调用该抽象方法时,都调用 beanFactory#getBean 重新获取对象。

    1. 工作原理

    现在我们先大致看一下 AutowiredAnnotationBeanPostProcessor 是如何工作的呢?beanFactory#doCreateBean 在创建 bean 过程中依次调用如下方法:

    • determineCandidateConstructors:解析类的构造器,用于处理构造器注入。如果构造器上标注有 @Autowired 注解,或只有一个有参构造器,则采用构造器自动注入。否则完全按照默认的配置参数 bd. constructorArgumentValues 实例化对象,或无参构造器实例化对象。
    • postProcessMergedBeanDefinition:配合 postProcessPropertyValues 方法一起处理字段或方法注入。解析标注有 @Autowired 的注入点元信息 InjectionMetadata,底层调用 findAutowiringMetadata 方法解析注入点元信息。
    • postProcessPropertyValues:将 postProcessMergedBeanDefinition 阶段解析的 InjectionMetadata 依次进行属性注入。
    AbstractAutowireCapableBeanFactory#doCreateBean
        -> createBeanInstance
            -> determineConstructorsFromBeanPostProcessors
                -> AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors
            -> autowireConstructor
                -> ConstructorResolver#autowireConstructor
        -> applyMergedBeanDefinitionPostProcessors
            -> AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
        -> populateBean
            -> AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
    

    2. determineCandidateConstructors

    determineCandidateConstructors 处理构造器注入的场景。该方法解析类的构造器,如果构造器上标注有 @Autowired 注解,或只有一个有参构造器,则采用构造器自动注入。否则完全按照默认的配置参数 bd. constructorArgumentValues 实例化对象,或无参构造器实例化对象。

    • @Lookup:实例化时使用 CglibSubclassingInstantiationStrategy 进行字节码提升,生成代理对象。

    • @Autowired:如果构造器上标注有 @Autowired(required=false) 注解,则添加到候选构造器 candidates 中,最后再将默认的无参构造器(如果存在)添加到 candidates 中返回即可。

      但如果有标注 @Autowired 的候选构造器,则标注有 @Autowired 注解的候选构造器只能有一个,并最终返回这个候选构造器。

    • 无 @Autowired:如果只有一个有参构造器,则直接返回这个构造器即可。如果有多个构造器或只有一个默认构造器,则返回 null。

    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException {
        // 1. 校验@Lookup 注解 省略...
        // 2. 解析@Autowire标注的构造器
        Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
        if (candidateConstructors == null) {
            synchronized (this.candidateConstructorsCache) {
                candidateConstructors = this.candidateConstructorsCache.get(beanClass);
                if (candidateConstructors == null) {
                    Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
                    List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
                    Constructor<?> requiredConstructor = null;
                    Constructor<?> defaultConstructor = null;
                    int nonSyntheticConstructors = 0;
                    // 3.1 遍历所有的构造器
                    for (Constructor<?> candidate : rawCandidates) {
                        // 3.2 构造器上是否标注有@Autowire,注意CGLIB代理
                        MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
                        if (ann == null) {
                            Class<?> userClass = ClassUtils.getUserClass(beanClass);
                            if (userClass != beanClass) {
                                Constructor<?> superCtor =  userClass.getDeclaredConstructor(candidate.getParameterTypes());
                                ann = findAutowiredAnnotation(superCtor);
                            }
                        }
                        // 3.3 如果标注@Autowire,进一步判断是否require=true,如果为true,只能有一个
                        if (ann != null) {
                            if (requiredConstructor != null) {
                                throw new BeanCreationException();
                            }
                            boolean required = determineRequiredStatus(ann);
                            if (required) {
                                if (!candidates.isEmpty()) {
                                    throw new BeanCreationException();
                                }
                                requiredConstructor = candidate;
                            }
                            candidates.add(candidate);
                        // 3.4 缓存默认构造器 
                        } else if (candidate.getParameterCount() == 0) {
                            defaultConstructor = candidate;
                        }
                    }
                    // 3.5   结果处理
                    // 3.5.1 标注@Autowire,如果require=true直接返回。否则添加无参构造器返回
                    if (!candidates.isEmpty()) {
                        if (requiredConstructor == null) {
                            if (defaultConstructor != null) {
                                candidates.add(defaultConstructor);
                            }
                        }
                        candidateConstructors = candidates.toArray(new Constructor<?>[0]);
                    // 3.5.2 无@Autowire。如果只有一个有参构造器,返回这个构造器,自动注入
                    } else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
                        candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
                    // 3.5.3 无@Autowire。如果有多个构造器或只有一个无参构造器,返回null
                    } else {
                        candidateConstructors = new Constructor<?>[0];
                    }
                    this.candidateConstructorsCache.put(beanClass, candidateConstructors);
                }
            }
        }
        return (candidateConstructors.length > 0 ? candidateConstructors : null);
    }
    

    说明: determineCandidateConstructors 解析类的构造器,我们主要看一下结果是如何处理的。

    1. 有构造器上标注 @Autowire。根据属性值 require 又可以分为两种情况,指定是否是必须的构造器,默认为 true。
      • require=true:只能有一个构造器设置为必须构造器,直接使用这个构造器实例化对象。
      • require=false:可以标注多个候选构造器,需要根据参数进一步匹配具体的构造器。此时,会添加默认的无参构造器。
    2. 没有构造器标注 @Autowire。也可以分为两种情况:
      • 只有一个有参构造器:直接返回这个有参构造器。
      • 有多个构造器或只有一个无参构造器:返回 null。此时需要根据 bd 配置来实例化对象。
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        ...
        // 1. 解析是否有构造器可用,当 ctors!=null 时采用即构造器自动注入
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        // 2. 有参构造器实例化(大部分情况),采用即构造器自动注入
        if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return autowireConstructor(beanName, mbd, ctors, args);
        }
        // 3. 无参构造器实例化
        return instantiateBean(beanName, mbd);
    }
    

    3. postProcessMergedBeanDefinition

    postProcessMergedBeanDefinition 和 postProcessMergedBeanDefinition 处理字段或方法注入的场景。postProcessMergedBeanDefinition 方法将标注 @Autowired 注入点(字段或方法)解析成元信息 InjectionMetadata,postProcessMergedBeanDefinition 则根据元信息 InjectionMetadata 注入到 bean 中。

    字段或方法注入相关方法说明:

    • postProcessMergedBeanDefinition:将 @Autowired 注入点(字段或方法)解析成元信息 InjectionMetadata。
    • findAutowiringMetadata:缓存解析后的注入点元信息到 injectionMetadataCache。
    • buildAutowiringMetadata:递归遍历所有的字段和方法,将标注 @Autowired 的注入点解析成 InjectionMetadata。该方法不会解析静态字段,所以 @Autowired 无法注入静态字段。
      • 字段:AutowiredFieldElement
      • 方法:AutowiredMethodElement
    • InjectionMetadata#checkConfigMembers:将已经处理过的注入点缓存到 bd.externallyManagedConfigMembers 中,下次再处理时不会处理已经缓存的注入点。
    • InjectionMetadata#inject:依次遍历所有的注入点元信息 InjectedElement,进行属性注入。
    AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
        -> findAutowiringMetadata
            -> buildAutowiringMetadata
        -> InjectionMetadata#checkConfigMembers
    AutowiredAnnotationBeanPostProcessor#postProcessProperties
        -> InjectionMetadata#inject
    

    3.1 buildAutowiringMetadata

    buildAutowiringMetadata 方法递归遍历所有的字段和方法,将标注 @Autowired 的注入点解析成 InjectionMetadata。该方法不会解析静态字段,所以 @Autowired 无法注入静态字段。方法本身并不难理解,最重要的关心解析后的对象 AutowiredFieldElement 和 AutowiredMethodElement。

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        // 1. 校验,如果clazz是JDK中的类,直接忽略,因为不可能标注有这些标注
        if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
            return InjectionMetadata.EMPTY;
        }
    
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;
    
        // 递归循环所有的父类,所有@Autowired父类的字段也会自动注入
        do {
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
    
            // 2. 解析所有字段上的注解,封装成AutowiredFieldElement,不包括static字段
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                MergedAnnotation<?> ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            });
    
            // 3. 解析所有方法上的注解,封装成AutowiredMethodElement,不包括static方法
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                // 3.1 处理桥接方法,先忽略这部分
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    // 3.2 忽略static方法
                    if (Modifier.isStatic(method.getModifiers())) {
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    // 3.3 如果是JavaBean字段,则返回pd,否则返回null
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            });
            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        } while (targetClass != null && targetClass != Object.class);
        return InjectionMetadata.forElements(elements, clazz);
    }
    

    说明: buildAutowiringMetadata 字段和方法注入点的元信息解析大同小异:

    1. 递归解析所有的父类的字段和方法,所以父类可以通过 @Autowired 注入。
    2. 不会解析静态字段或方法,所以静态字段无法通过 @Autowired 注入。
    3. 字段和方法分别解析为 AutowiredFieldElement 和 AutowiredMethodElement。其中有两个重要的属性:一个是注入点 Member,二是注入点是否必须。

    4. postProcessPropertyValues

    postProcessMergedBeanDefinition 方法根据元信息 InjectionMetadata,在 Spring IoC 容器中查找依赖注入到 bean 中。该方法完全委托给了 InjectionMetadata#inject 方法:

    public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        Collection<InjectedElement> checkedElements = this.checkedElements;
        Collection<InjectedElement> elementsToIterate =
            (checkedElements != null ? checkedElements : this.injectedElements);
        if (!elementsToIterate.isEmpty()) {
            for (InjectedElement element : elementsToIterate) {
                element.inject(target, beanName, pvs);
            }
        }
    }
    

    说明: inject 方法就是循环所有的注入点,依次调用其 inject 进行属性注入。问题的关键是 checkedElements 是什么,也就是说会注入那些字段?在之前分析 postProcessMergedBeanDefinition 时,提到调用 findAutowiringMetadata 解析完注入点元信息后,会调用 InjectionMetadata#checkConfigMembers 校验。校验到底是做什么的呢?

    public void checkConfigMembers(RootBeanDefinition beanDefinition) {
        Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
        for (InjectedElement element : this.injectedElements) {
            Member member = element.getMember();
            if (!beanDefinition.isExternallyManagedConfigMember(member)) {
                beanDefinition.registerExternallyManagedConfigMember(member);
                checkedElements.add(element);
            }
        }
        this.checkedElements = checkedElements;
    }
    

    说明: bd.externallyManagedConfigMembers 缓存已经校验过的注入点,这些缓存的注入点不会再次进行注入,目的就是为了避免重复注入的问题。那问题就来了,字段怎么会进行重复注入呢?比如 CommonAnnotationBeanPostProcessor 同样会解析注入点的元信息,如果 @Autowired 和 @Resource 出现在同一个字段上,此时会出现重复注入的情况。

    下面对 InjectionMetadata 中两个注入点属性进行一下说明:

    • injectedElements:所有解析的注入点元信息 InjectedElement。
    • checkedElements:需要进行属性注入的注入元信息,剔除 bd.externallyManagedConfigMembers 已经处理的注入点。

    下面会对字段注入和方法注入,分别进行分析。关键是如何进行依赖查找,底层最终都是调用 beanFactory#resolveDependency 方法。

    5. AutowiredFieldElement

    字段注入时,通常根据字段类型和字段名称查找依赖。当然,如果你使用 @Value("#{beanName}") 时,会读取注解中的值进行解析。核心还是 beanFactory#resolveDependency 方法。方法本身很简单,都不多说了。

    @Override
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        Field field = (Field) this.member;
        Object value;
        // 从缓存中提取value值,可能为desc、beanName、value值
        if (this.cached) {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        } else {
            DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
            desc.setContainingClass(bean.getClass());
            Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
            synchronized (this) {
                // 只要是解析过,不会缓存下来
                if (!this.cached) {
                    if (value != null || this.required) {
                        // 缓存DependencyDescriptor
                        this.cachedFieldValue = desc;
                        registerDependentBeans(beanName, autowiredBeanNames);
                        // 缓存名称beanName,直接根据名称查找
                        if (autowiredBeanNames.size() == 1) {
                            String autowiredBeanName = autowiredBeanNames.iterator().next();
                            if (beanFactory.containsBean(autowiredBeanName) &&
                                beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                    desc, autowiredBeanName, field.getType());
                            }
                        }
                    } else {
                        this.cachedFieldValue = null;
                    }
                    this.cached = true;
                }
            }
        }
        // java反射
        if (value != null) {
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }
    

    6. AutowiredMethodElement

    基本上和 AutowiredFieldElement 雷同,唯一不同的是方法注入时,需要对方法的所以参数依次调用 beanFactory#resolveDependency 进行依赖查找。根据 require 值,如果为 true 时无法查找到依赖时会继续查找,false 则不再进行查找。一般默认为 true。


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

  • 相关阅读:
    Proxy 相对于 Object.defineProperty 有哪些优点?
    Vue 3.0 所采用的 Composition Api 与 Vue 2.x使用的Options Api 有什么区别?
    Vue 3.0 性能提升主要是通过哪几个方面体现的?
    封装 Vue 组件库
    rollup-plugin-postcss ( PostCSS plugin postcss-noop-plugin requires PostCSS 8. Migration guide for end-users:)
    LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
    vue serve 命令不管用
    典型80后的5年工作总结
    Elasticsearch强大的聚合功能Facet
    Mongodb使用总结
  • 原文地址:https://www.cnblogs.com/binarylei/p/12342100.html
Copyright © 2011-2022 走看看