Spring 注解原理(三)AutowireCandidateResolver:@Qualifier @Value @Autowire @Lazy

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

1. AutowireCandidateResolver 接口

AutowireCandidateResolver 用来判断一个给定的 bean 是否可以注入,最主要的方法是 isAutowireCandidate。简单来说 isAutowireCandidate 就根据 @Qualifier 添加过滤规则来判断 bean 是否合法。

  1. public interface AutowireCandidateResolver {
  2. // 判断给定的 bdHolder 是否可以注入 descriptor,BeanDefinition#autowireCandidate 默认为 true
  3. // DependencyDescriptor 是对字段、方法、参数的封装,便于统一处理,这里一般是对属性写方法参数的封装
  4. default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
  5. return bdHolder.getBeanDefinition().isAutowireCandidate();
  6. }
  7. // @since 5.0 判断是否必须注入,如果是字段类型是 Optional 或有 @Null 注解时为 false
  8. default boolean isRequired(DependencyDescriptor descriptor) {
  9. return descriptor.isRequired();
  10. }
  11. // @since 5.1 判断是否有 @Qualifier(Spring 或 JDK) 或自定义的注解
  12. default boolean hasQualifier(DependencyDescriptor descriptor) {
  13. return false;
  14. }
  15. // @Value 时直接返回
  16. default Object getSuggestedValue(DependencyDescriptor descriptor) {
  17. return null;
  18. }
  19. // @since 4.0
  20. default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
  21. return null;
  22. }
  23. }

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 注入配置属性

  1. @Value("${jdbd.url}")
  2. private String url;

@Value 的处理方式如下,查找到 @Value 的值作为 AutowireCandidateResolver#getSuggestedValue 的返回值。

  1. private Class<? extends Annotation> valueAnnotationType = Value.class;
  2. @Override
  3. public Object getSuggestedValue(DependencyDescriptor descriptor) {
  4. // 1. 先查找字段或方法参数上的注解
  5. Object value = findValue(descriptor.getAnnotations());
  6. if (value == null) {
  7. // 2. 查找方法上的注解
  8. MethodParameter methodParam = descriptor.getMethodParameter();
  9. if (methodParam != null) {
  10. value = findValue(methodParam.getMethodAnnotations());
  11. }
  12. }
  13. return value;
  14. }

说明: getSuggestedValue 方法先处理注入点(包括字段或方法参数上)的注解信息,如果没有再查找方法上的注解。如下,@Value 和 @Qualifier 两个注解,方法参数上的注解都优先于方法上的注解。

  1. @Value("${name1}")
  2. public void setName(@Value("${name2}") String name) {
  3. ...
  4. }

2.2 @Autowire 处理

  1. @Override
  2. public boolean isRequired(DependencyDescriptor descriptor) {
  3. if (!super.isRequired(descriptor)) {
  4. return false;
  5. }
  6. Autowired autowired = descriptor.getAnnotation(Autowired.class);
  7. return (autowired == null || autowired.required());
  8. }

说明: @Autowired 默认 require=true。

2.3 @Qualifier 处理

  1. @Autowire
  2. @Qualifier("user")
  3. private User user;

3. @Qualifier 源码分析

在使用 Spring 框架中进行自动注人时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常。Spring 允许我们通过 Qualifier 指定注入 Bean 的名称,这样歧义就消除了。@Qualifier 四种用法介绍

3.1 <qualifier> 标签解析

(1)普通 Bean 定义:不带任何 <qualifier> 标签(默认)

  1. <bean id="user1" class="com.binarylei.spring.ioc.domain.User">

匹配时默认匹配 User Bean 的名称。即:

  1. @Qualifier("user1")
  2. private User user;

(2)Bean 定义:配置 <qualifier> 标签

  1. <bean id="user2" class="com.binarylei.spring.ioc.domain.User">
  2. <qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="xxxx">
  3. <attribute key="name" value="binarylei"/>
  4. <attribute key="age" value="20"/>
  5. </qualifier>
  6. <qualifier type="org.springframework.beans.factory.annotation.Qualifier2" value="xxxx">
  7. <attribute key="name" value="binarylei"/>
  8. </qualifier>
  9. </bean>

说明: <qualifier> 标签可以有多个,如果 type 类型相同,后面的会覆盖前面的。每个 <qualifier> 最终会解析成 AutowireCandidateQualifier,然后添加 AbstractBeanDefinition 中 Map<String, AutowireCandidateQualifier> qualifiers;

  1. private final String typeName;
  2. private final Map<String, Object> attributes = new LinkedHashMap<>();
  3. 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 的简单扩展,支持分组注入。

  1. @Qualifier
  2. public @interface LoadBalanced {
  3. }

(3)Bean 定义:注解驱动

  1. @Bean
  2. @Qualifier(value = "uri1")
  3. private URI uri1() {
  4. return URI.create("http://www.baidu.com");
  5. }

注意: 注解驱动不会将 @Qualifier 解析到 bd.qualifiers 中,会直接读取注入点的 @Qualifier 注解,使用 SynthesizedMergedAnnotationInvocationHandler#annotationEquals 直接比较两个注解的属性是否全部相等。

(4)Bean 定义:自定义注解,分组注入

  1. @Bean
  2. @LoadBalanced
  3. private URI uri1() {
  4. return URI.create("http://www.baidu.com");
  5. }
  6. @Qualifier("user1")
  7. private URI uri;

说明: @Qualifier 注解有一个特性,如果在注入的字段上加上 @Qualifier 注解,则会将定义 Bean 时标注了 @Qualifier 的对象注入进来。其派生注解 @LoadBalanced 也有同样的特性。当然 @Qualifier 的这种特性本质上一种过滤规则,后面分析源码时会分析。

3.2 默认处理 Spring 和 JDK 的 @Qualifier 注解

  • @org.springframework.beans.factory.annotation.Qualifier
  • @javax.inject.Qualifier
  1. private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
  2. public QualifierAnnotationAutowireCandidateResolver() {
  3. this.qualifierTypes.add(Qualifier.class);
  4. // JSR-330
  5. this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
  6. }

在分析 @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 注解,也就会有多个属性键值对。

  1. @Override
  2. public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
  3. boolean match = super.isAutowireCandidate(bdHolder, descriptor);
  4. if (match) {
  5. // 1. 先查找字段或方法参数上的注解
  6. match = checkQualifiers(bdHolder, descriptor.getAnnotations());
  7. if (match) {
  8. MethodParameter methodParam = descriptor.getMethodParameter();
  9. if (methodParam != null) {
  10. Method method = methodParam.getMethod();
  11. if (method == null || void.class == method.getReturnType()) {
  12. // 2. 查找方法上的注解
  13. match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
  14. }
  15. }
  16. }
  17. }
  18. return match;
  19. }

说明: 同 @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。
  1. protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
  2. if (ObjectUtils.isEmpty(annotationsToSearch)) {
  3. return true;
  4. }
  5. SimpleTypeConverter typeConverter = new SimpleTypeConverter();
  6. for (Annotation annotation : annotationsToSearch) {
  7. Class<? extends Annotation> type = annotation.annotationType();
  8. boolean checkMeta = true;
  9. boolean fallbackToMeta = false;
  10. // 1. 如果本身是@Qualifier注解,且匹配成功,则不需要解析元注解
  11. if (isQualifier(type)) {
  12. if (!checkQualifier(bdHolder, annotation, typeConverter)) {
  13. fallbackToMeta = true;
  14. } else {
  15. checkMeta = false;
  16. }
  17. }
  18. // 2. 两种情况需要解析元注解:一是本身不是@Qualifier注解,二是匹配失败
  19. if (checkMeta) {
  20. boolean foundMeta = false;
  21. // 2.1 遍历元注解,即注解上的注解。只遍历一层元注解。
  22. for (Annotation metaAnn : type.getAnnotations()) {
  23. Class<? extends Annotation> metaType = metaAnn.annotationType();
  24. // 2.2 元注解是@Qualifier注解
  25. if (isQualifier(metaType)) {
  26. foundMeta = true;
  27. // 1. fallbackToMeta=true说明第一次匹配失败,此时元注解必须定义value值?
  28. // 2. 元注解匹配失败
  29. if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
  30. !checkQualifier(bdHolder, metaAnn, typeConverter)) {
  31. return false;
  32. }
  33. }
  34. }
  35. // 2.3 两次匹配失败,才算匹配失败
  36. // fallbackToMeta=true表示第一次匹配失败,如果第一次匹配成功就直接返回true了
  37. // foundMeta=true表示第二次匹配成功,因为如果匹配失败,则已经返回false
  38. if (fallbackToMeta && !foundMeta) {
  39. return false;
  40. }
  41. }
  42. }
  43. return true;
  44. }

说明: checkQualifiers 对注入点上的 @Qualifier(包派生注解) 注解逐一匹配,只要有匹配失败的返回 false。checkQualifiers 最显要的特性是支持 @Qualifier 派生注解,匹配的规则看起来就比较复杂。大部分场景(包括自定义注解),只需要进行第一次匹配即可。

下面介绍一下 @Qualifier 派生注解。因为注解是没有继承或实现一说了,为了复用注解,Spring 提出了派生注解的概念。在本例中,@MyQualifier1 是 @Qualifier 派生而来,@MyQualifier2 是 @MyQualifier1 派生而来。

  1. @Qualifier
  2. private static @interface MyQualifier1 {
  3. }
  4. @MyQualifier1
  5. private static @interface MyQualifier2 {
  6. }
  7. @MyQualifier1
  8. @Qualifier
  9. private static @interface MyQualifier3 {
  10. }

isQualifier 方法,已经考虑了自定义的派生注解情况,也就是 @MyQualifier1 会直接匹配,只有 @MyQualifier2 则是元注解匹配。@MyQualifier3 则可能先直接匹配再元注解匹配。如此可见,大部分场景,我们也用不到二次匹配,也不会定义这么复杂的注解。

  1. protected boolean isQualifier(Class<? extends Annotation> annotationType) {
  2. for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
  3. if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
  4. return true;
  5. }
  6. }
  7. return false;
  8. }

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。
  1. protected boolean checkQualifier(
  2. BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
  3. Class<? extends Annotation> type = annotation.annotationType();
  4. RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
  5. // 1. 获取 BeanDefinition 中的 bd.qualifier
  6. AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
  7. if (qualifier == null) {
  8. qualifier = bd.getQualifier(ClassUtils.getShortName(type));
  9. }
  10. // 2. 注解驱动配置:bd.qualifier=null 一般为注解驱动,targetAnnotation.equals(annotation)
  11. if (qualifier == null) {
  12. // 2.1 db.qualifiedElement 一般没有赋值,不会使用
  13. Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type)
  14. // 2.2 db.factoryMethodToIntrospect 获取工厂方法上的注解,@Bean配置方式,主要获取方式
  15. if (targetAnnotation == null) {
  16. targetAnnotation = getFactoryMethodAnnotation(bd, type);
  17. }
  18. // 2.3 db.decoratedDefinition
  19. if (targetAnnotation == null) {
  20. RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
  21. if (dbd != null) {
  22. targetAnnotation = getFactoryMethodAnnotation(dbd, type);
  23. }
  24. }
  25. // 2.4 尝试在对象类型上获取@Qualifier,之前的方式都是在Bean定义的位置获取
  26. if (targetAnnotation == null) {
  27. if (getBeanFactory() != null) {
  28. Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
  29. if (beanType != null) {
  30. targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
  31. }
  32. }
  33. if (targetAnnotation == null && bd.hasBeanClass()) {
  34. targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);
  35. }
  36. }
  37. // 2.5 将”Bean元信息注解"和"注入点注解"属性进行比较
  38. if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
  39. return true;
  40. }
  41. }
  42. // 2. 常量 XML 配置:bd.qualifier 此时不为空,除非没有配置<qualifier>标签
  43. Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
  44. // 2.1 attributes.isEmpty()为空肯定是自定义注解,否则@Qualifier至少有value=""的属性
  45. // 此时有自定义注解,却bd.qualifier=null,肯定无法匹配
  46. if (attributes.isEmpty() && qualifier == null) {
  47. return false;
  48. }
  49. // 2.2 注解attributes和bd.qualifier属性值进行匹配
  50. // db.qualifier.attributeName -> db.attributeName ->
  51. // beanName -> @Qualifier.defaultValue
  52. for (Map.Entry<String, Object> entry : attributes.entrySet()) {
  53. String attributeName = entry.getKey();
  54. Object expectedValue = entry.getValue();
  55. Object actualValue = null;
  56. if (qualifier != null) {
  57. actualValue = qualifier.getAttribute(attributeName);
  58. }
  59. if (actualValue == null) {
  60. actualValue = bd.getAttribute(attributeName);
  61. }
  62. // 默认和beanName进行比较
  63. if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
  64. expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
  65. continue;
  66. }
  67. if (actualValue == null && qualifier != null) {
  68. actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
  69. }
  70. if (actualValue != null) {
  71. actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
  72. }
  73. if (!expectedValue.equals(actualValue)) {
  74. return false;
  75. }
  76. }
  77. return true;
  78. }

说明: 总结一下,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。
  1. protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
  2. // 1. 依赖类型 dependencyType
  3. ResolvableType dependencyType = descriptor.getResolvableType();
  4. // 2. 依赖类型没有泛型,直接返回。因为既然调用该方法,那就是根据类型查找依赖,class已经匹配过
  5. if (dependencyType.getType() instanceof Class) {
  6. return true;
  7. }
  8. ResolvableType targetType = null;
  9. boolean cacheType = false;
  10. RootBeanDefinition rbd = null;
  11. if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
  12. rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
  13. }
  14. // 3. 工厂方法创建,查找实际注入的类型,如 @Bean
  15. // 注意:工厂方法解析的类型和依赖类型dependencyType不匹配时,返回null
  16. if (rbd != null) {
  17. targetType = rbd.targetType;
  18. if (targetType == null) {
  19. cacheType = true;
  20. targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
  21. if (targetType == null) {
  22. RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
  23. if (dbd != null) {
  24. targetType = dbd.targetType;
  25. if (targetType == null) {
  26. targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
  27. }
  28. }
  29. }
  30. }
  31. }
  32. // 4. 非工厂方法创建,beanFactor#getType -> bd.beanClass
  33. if (targetType == null) {
  34. // 4.1 getType 会直接获取实例的类型,再读取bd信息
  35. if (this.beanFactory != null) {
  36. Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
  37. if (beanType != null) {
  38. targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanType));
  39. }
  40. }
  41. // 4.2 Fallback: no BeanFactory set, or no type resolvable。读取 bd.beanClass
  42. if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
  43. Class<?> beanClass = rbd.getBeanClass();
  44. if (!FactoryBean.class.isAssignableFrom(beanClass)) {
  45. targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
  46. }
  47. }
  48. }
  49. // 5. 结果配置
  50. // 5.1 targetType==null
  51. if (targetType == null) {
  52. return true;
  53. }
  54. if (cacheType) {
  55. rbd.targetType = targetType;
  56. }
  57. // 5.2 descriptor.fallbackMatchAllowed表示精确匹配时匹配失败,回退至泛型无法解析
  58. if (descriptor.fallbackMatchAllowed() &&
  59. (targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
  60. return true;
  61. }
  62. // 5.3 泛型直接匹配
  63. return dependencyType.isAssignableFrom(targetType);
  64. }

说明: 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 处理

  1. @Autowired
  2. @Lazy
  3. private Environment environmentLazy; // 实际注入一个代理对象

5.2 源码分析

  1. @Override
  2. public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
  3. return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
  4. }

说明: isLazy 方法和 @Value 、@Autowire 、@Qualifier 处理都差不多,我们看一下 buildLazyResolutionProxy 方法,其实也很简单,无非是生成一个代理对象。核心就一句代码 beanFactory.doResolveDependency。

  1. protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
  2. final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
  3. TargetSource ts = new TargetSource() {
  4. @Override
  5. public Class<?> getTargetClass() {
  6. return descriptor.getDependencyType();
  7. }
  8. @Override
  9. public boolean isStatic() {
  10. return false;
  11. }
  12. @Override
  13. public Object getTarget() {
  14. Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
  15. if (target == null) {
  16. Class<?> type = getTargetClass();
  17. if (Map.class == type) {
  18. return Collections.emptyMap();
  19. }
  20. else if (List.class == type) {
  21. return Collections.emptyList();
  22. }
  23. else if (Set.class == type || Collection.class == type) {
  24. return Collections.emptySet();
  25. }
  26. throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
  27. "Optional dependency not present for lazy injection point");
  28. }
  29. return target;
  30. }
  31. @Override
  32. public void releaseTarget(Object target) {
  33. }
  34. };
  35. ProxyFactory pf = new ProxyFactory();
  36. pf.setTargetSource(ts);
  37. Class<?> dependencyType = descriptor.getDependencyType();
  38. if (dependencyType.isInterface()) {
  39. pf.addInterface(dependencyType);
  40. }
  41. return pf.getProxy(beanFactory.getBeanClassLoader());
  42. }

说明: ProxyFactory 是 spring-aop 中生成代理对象的工具类,不在本文的讨论范围内。如果 dependencyType 是接口,使用 JDK 动态代理,否则使用 CGLIB 代理。


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

Spring 注解原理(三)@Qualifier @Value的更多相关文章

  1. Spring 注解原理(一)组件注册

    Spring 注解原理(一)组件注册 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 当我们需要使用 Spring 提供的 ...

  2. Spring注解原理

    一.注解的基本概念和原理及其简单实用 注解(Annotation)提供了一种安全的类似注释的机制,为我们在代码中添加信息提供了一种形式化得方法,使我们可以在稍后某个时刻方便的使用这些数据(通过解析注解 ...

  3. [转]Spring注解原理的详细剖析与实现

    原文地址:http://freewxy.iteye.com/blog/1149128/ 本文主要分为三部分: 一.注解的基本概念和原理及其简单实用 二.Spring中如何使用注解 三.编码剖析spri ...

  4. Spring注解原理的详细剖析与实现

    本文主要分为三部分: 一. 注解的基本概念和原理及其简单实用 二. Spring中如何使用注解 三. 编码剖析spring@Resource的实现原理 一.注解的基本概念和原理及其简单实用 注解(An ...

  5. Spring注解@Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier、@scope

    以下内容摘自部分网友的,并加上了自己的理解 @Service用于标注业务层组件(我们通常定义的service层就用这个) @Controller用于标注控制层组件(如struts中的action.Sp ...

  6. 【Spring注解驱动开发】使用@Autowired@Qualifier@Primary三大注解自动装配组件,你会了吗?

    写在前面 [Spring专题]停更一个多月,期间在更新其他专题的内容,不少小伙伴纷纷留言说:冰河,你[Spring专题]是不是停更了啊!其实并没有停更,只是中途有很多小伙伴留言说急需学习一些知识技能, ...

  7. 【Spring注解驱动开发】自定义组件如何注入Spring底层的组件?看了这篇我才真正理解了原理!!

    写在前面 最近,很多小伙伴出去面试都被问到了Spring问题,关于Spring,细节点很多,面试官也非常喜欢问一些很细节的技术点.所以,在 Spring 专题中,我们尽量把Spring的每个技术细节说 ...

  8. 【spring 注解驱动开发】Spring AOP原理

    尚学堂spring 注解驱动开发学习笔记之 - AOP原理 AOP原理: 1.AOP原理-AOP功能实现 2.AOP原理-@EnableAspectJAutoProxy 3.AOP原理-Annotat ...

  9. Annotation(三)——Spring注解开发

    Spring框架的核心功能IoC(Inversion of Control),也就是通过Spring容器进行对象的管理,以及对象之间组合关系的映射.通常情况下我们会在xml配置文件中进行action, ...

随机推荐

  1. sql语句查询菜单结果成 树状图类型 注意适用于id是四位数

    select * from ( select pid,id,name,url,concat(id,":") idOrder from menu where pid=0 and st ...

  2. metasploitable使用

    DVWA默认的用户有5个,用户名密码如下(一个足以): admin/password gordonb/abc123 1337/charley pablo/letmein smithy/password

  3. sql根据最小值去重

    CREATE TABLE temp2 AS SELECT MAX(id) id FROM sys_oper_procenter GROUP BY pro_title 创建一个temp2的表 根据标题分 ...

  4. 简单的jQ代码

    简单的jQ代码 /* * Lazy Load - jQuery plugin for lazy loading images * * Copyright (c) 2007-2012 Mika Tuup ...

  5. MongoDB之Limit选取Skip跳过Sort排序

    1.Limit选取 我要从Document中取出多少个 只要2条Document db.Wjs.find().limit(2) 2.Skip跳过 我要跳过多少个Document 我要跳过前两个Docu ...

  6. Codeforces Beta Round #61 (Div. 2)

    Codeforces Beta Round #61 (Div. 2) http://codeforces.com/contest/66 A 输入用long double #include<bit ...

  7. P3375 【模板】KMP字符串匹配

    P3375 [模板]KMP字符串匹配 https://www.luogu.org/problemnew/show/P3375 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在 ...

  8. ES3之cookie

    一 cookie的作用域由文档源(domain)和文档路径(path)决定. 当前页面的脚本只能操作:当前源的当前路径的cookie.当前源的父路径的cookie. 向服务器发送请求时,请求头(Req ...

  9. Mac安装MySQL数据库

    一 下载及安装社区版MySQL和MySQL Workbench. 二 如果MySQL Workbench无法登陆,则系统偏好设置-MySQL-Initialize Database,选择legacy开 ...

  10. Mac下Chrome浏览器的手机模拟器,开启模拟定位

    项目接入百度地图,浏览器调试时需要获取定位信息. 1 打开设置->高级->内容设置->位置,将定位设置为“使用前先询问”,并清空禁止列表. 2 审查元素->Network-&g ...