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。


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

posted on 2020-02-21 17:09  binarylei  阅读(2776)  评论(2编辑  收藏  举报

导航