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


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

  • 相关阅读:
    【crontab】误删crontab及其恢复
    New Concept English there (7)
    New Concept English there (6)
    New Concept English there (5)
    New Concept English there (4)
    New Concept English there (3)
    New Concept English there (2)Typing speed exercise
    New Concept English there (1)Typing speed exercise
    New Concept English Two 34 game over
    New Concept English Two 33 94
  • 原文地址:https://www.cnblogs.com/binarylei/p/12342100.html
Copyright © 2020-2023  润新知