<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          社區(qū)精選|Spring 中 @Qualifier 注解還能這么用?

          共 30824字,需瀏覽 62分鐘

           ·

          2023-08-19 15:29

          今天小編為大家?guī)淼氖巧鐓^(qū)作者 江南一點雨 的文章,讓我們一起來學習Spring 中 @Qualifier 注解。




          今天想和小伙伴們聊一聊 @Qualifier 注解的完整用法,同時也順便分析一下它的實現(xiàn)原理。

          說到 @Qualifier,有的小伙伴可能會覺得詫異,這也只得寫一篇文章?確實,但凡有點開發(fā)經(jīng)驗,多多少少可能都遇到過 @Qualifier 注解的使用場景,然而,對于大部分小伙伴來說,我們平時開發(fā)遇到的 @Qualifier 注解使用場景,只是 @Qualifier 注解功能中很小的一部分而已,今天咱們就來完整的捋一捋。


          1. 基本用法

          首先和小伙伴們回顧一下 @Qualifier 注解的基本用法,基本用法我從四個方面來和大家介紹,只有先把這些基本用法捋清楚了,在看源碼的時候才會有種醍醐灌頂?shù)母杏X。

          1.1 指定 Bean 名稱


          說到 @Qualifier 注解,大家最容易想到的就是處理 Bean 注入的問題了,假設我有如下 Bean:
             
          @Configuration
          @ComponentScan
          public class JavaConfig {
              @Bean(value = "b1")
              B b1() {
                  return new B();
              }

              @Bean("b2")
              B b2() {
                  return new B();
              }
          }

          將 B 向 Spring 容器中注冊了兩個,名字分別是 b1 和 b2。

          現(xiàn)在在 A 中想要使用 B,如下:
             
          @Component
          public class A {
              @Autowired
              B b;
          }

          由于 @Autowired 注解是按照類型進行 Bean 的注入的,此時 Spring 容器中存在兩個 B 實例,那么注入就會出錯,通過 @Qualifier 注解我們可以指定具體想要使用哪一個 Bean:
             
          @Component
          public class A {
              @Autowired
              @Qualifier("b1")
              B b;
          }

          這樣就指定了在注入時使用 b1 這個對象了。

          當然,對于這個問題,其實解決方案很多,如使用 @Primary 注解、使用 @Bean 注解但是額外加配置等都能解決問題,不過本文主題是 @Qualifier,所以暫時先不和大家討論其它方案。


          1.2 不指定 Bean 名稱


          在 1.1 小節(jié)中,我們使用 @Qualifier 注解時指定了需要注入的 Bean 名稱,其實也可以不指定 Bean 名稱,不指定 Bean 名稱的話,我們就需要在兩個地方進行配置。

          首先在 Bean 注入的時候,添加 @Qualifier 注解:
             
          @Configuration
          @ComponentScan
          public class JavaConfig {
              @Bean(value = "b1")
              @Qualifier
              B b1() {
                  return new B();
              }

              @Bean("b2")
              B b2() {
                  return new B();
              }
          }

          大家看到,這里給 b1 添加了 @Qualifier 注解,但是未設置任何 value,然后在需要進行 B 對象注入的地方,也添加 @Qualifier 注解:
             
          @Component
          public class A {
              @Autowired
              @Qualifier
              B b;
          }

          這樣也能解決問題。


          1.3 自定義注解


          例如我可以自定義一個注解,專門用來注入 b1 對象的注解,如下:
             
          @Retention(RetentionPolicy.RUNTIME)
          @Inherited
          @Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
          @Qualifier
          public @interface B1Qualifier {
          }

          然后分別在注冊 Bean 和使用 Bean 的時候,添加該注解:
             
          @Configuration
          @ComponentScan
          public class JavaConfig {
              @Bean(value = "b1")
              @B1Qualifier
              B b1() {
                  return new B();
              }

              @Bean("b2")
              B b2() {
                  return new B();
              }
          }
          @Component
          public class A {
              @Autowired
              @B1Qualifier
              B b;
          }

          這樣也是一個問題解決辦法。


          1.4 XML 中的配置


          前面跟大家說的都是在 Java 代碼中進行配置的,我們也可以通過 XML 文件進行配置,并且在 XML 文件配置的過程中,還可以配置多個不同的屬性,我舉個例子。

          假設我現(xiàn)在準備向 Spring 容器中注入兩個 B,如下:
             
          <bean class="org.javaboy.bean.p3.B" id="b1">
              <qualifier value="b11" type="org.springframework.beans.factory.annotation.Qualifier">
              </qualifier>
          </bean>
          <bean class="org.javaboy.bean.p3.B" id="b2">
              <qualifier/>
          </bean>

          小伙伴們看到,在第一個 bean 標簽中,我加入了 qualifier 標簽,這個標簽的 value 是 b11,type 則是 @Qualifier 本身,這個 type 其實也可以不配置,不配置的話默認也是 @Qualifier 注解本身;在第二個 bean 標簽中我只加了 qualifier 標簽,并未配置任何屬性(相當于 1.2 小節(jié)的案例)。

          現(xiàn)在,當我想要在 A 中注入 B 的時候,可以按照如下方式來:
             
          @Component
          public class A {
              @Autowired
              @Qualifier("b11")
              B b;
          }

          大家注意,這里 @Qualifier 注解的 value 是 b11,對應了 qualifier 標簽中的 value 屬性,表示將 id 為 b1 的 Bean 注入到 A 中的 b 屬性上。

          如果沒有為 @Qualifier 設置 value,那么就會將 id 為 b2 的 Bean 注入進來,這個就相當于我們前面 1.2 小節(jié)的案例。

          前面我們使用的是 @Qualifier 注解中的 value 屬性,實際上,qualifier 標簽支持更多的屬性定義。但問題是 @Qualifier 注解只有一個 value 屬性,如果想要使用其它的屬性進行匹配,那么就得使用自定義注解了(當然,這種場景實際上使用較少)。

          如果想要自定義注解去匹配 qualifier 標簽中提供的多種屬性,那么我們可以按照如下方式來進行配置。

          首先我們自定義注解,如下:
             
          @Retention(RetentionPolicy.RUNTIME)
          @Inherited
          @Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
          @Qualifier
          public @interface MyQualifier {
              String name() default "";
          }

          這是一個組合注解,本質上還是 @Qualifier,但是現(xiàn)在多了一個我們自定義的 name 屬性。

          接下來在 XML 中使用該注解:
             
          <bean class="org.javaboy.bean.p3.B" id="b1">
              <qualifier type="org.javaboy.bean.p3.MyQualifier">
                  <attribute key="name" value="b11"/>
              </qualifier>
          </bean>
          <bean class="org.javaboy.bean.p3.B" id="b2">
              <qualifier type="org.javaboy.bean.p3.MyQualifier">
                  <attribute key="name" value="b22"/>
              </qualifier>
          </bean>

          接下來在 Bean 注入的時候,就可以使用 @MyQualifier 進行匹配了:
             
          @Component
          public class A {
              @Autowired
              @MyQualifier(name = "b11")
              B b;
          }

          這個就表示匹配 name 屬性為 b11 的 Bean。

          以上基本上就是 @Qualifier 注解在 Spring 容器中的一些用法了,接下來松哥將通過源碼分析,來和小伙伴們一起探討上面這些功能到底是怎么實現(xiàn)的。



          2. 源碼分析

          為了小伙伴們能輕松掌握 @Qualifier 的源碼,一些前置的步驟我這里就不和大家分析了,重點就看 @Qualifier 注解的處理過程,其他未盡內容,將在后續(xù)文章中我會繼續(xù)和大家分享。

          由于 @Qualifier 注解一般都是搭配 @Autowired 注解一起使用的,所以解析 @Qualifier 注解的源碼離不開 @Autowired 的注入過程,剛好松哥在之前的文章中已經(jīng)和小伙伴們聊過 @Autowired 注解的注入過程了,還沒看過該文章的小伙伴建議先閱讀該文章,這有助于理解接下來的內容。


          2.1 doResolveDependency


          @Autowired 到底是?怎么把變量注入進來的?的 3.3 小節(jié)中,我們提到,給 A 注入 B 的時候,會調用到 doResolveDependency 方法,我們再來看下該方法:

          DefaultListableBeanFactory#doResolveDependency:
             
          @Nullable
          public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
                  //...
                  Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
                  if (matchingBeans.isEmpty()) {
                      if (isRequired(descriptor)) {
                          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                      }
                      return null;
                  }
                  //...
          }

          在這個方法中,首先調用了 findAutowireCandidates 方法去找到所有滿足條件的 Class。Map 中的 key 就是 Bean 的名稱,value 則是一個 Class,此時還沒有實例化。

          對于 @Qualifier 注解的處理就在 findAutowireCandidates 方法中。

          2.2 findAutowireCandidates


             
          protected Map<String, Object> findAutowireCandidates(
                  @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
              String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                      this, requiredType, true, descriptor.isEager());
              //...
              for (String candidate : candidateNames) {
                  if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                      addCandidateEntry(result, candidate, descriptor, requiredType);
                  }
              }
              //...
              return result;
          }

          在這個方法中,首先調用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法查找出 B 這種類型的所有 beanName,對于本文一開始的案例來說,這里拿到兩個 beanName,分別是 b1、b2,如下圖:

          接下來就去遍歷 candidateNames,在遍歷的時候,有兩個判斷條件:

          1. isSelfReference:這個方法是判斷給定的 beanName 是否自引用,即是否指向原始 bean 或者原始 bean 上的工廠方法,這個判斷跟本文案例關系不大。
          2. isAutowireCandidate:這個方法從名字上就能看出來,判斷這個 beanName 是否是一個候選的注入 beanName,很明顯,這個跟本文案例相關,我們繼續(xù)來看該方法。

          在 isAutowireCandidate 方法,又依次調了三次 isAutowireCandidate 方法,也就是說 isAutowireCandidate 方法一共調了四次之后,將會來到關鍵的 QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate 方法中。

             
          @Override
          public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
              boolean match = super.isAutowireCandidate(bdHolder, descriptor);
              if (match) {
                  match = checkQualifiers(bdHolder, descriptor.getAnnotations());
                  if (match) {
                      MethodParameter methodParam = descriptor.getMethodParameter();
                      if (methodParam != null) {
                          Method method = methodParam.getMethod();
                          if (method == null || void.class == method.getReturnType()) {
                              match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
                          }
                      }
                  }
              }
              return match;
          }

          在當前方法中,首先會調用 super.isAutowireCandidate 方法去判斷這個 Bean 將來是否被允許注入到其他 Bean 中:
             
          @Override
          public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
              if (!super.isAutowireCandidate(bdHolder, descriptor)) {
                  // If explicitly falsedo not proceed with any other checks...
                  return false;
              }
              return checkGenericTypeMatch(bdHolder, descriptor);
          }

          這里又是兩件事,第一個是調用父類方法進行判斷,這里單純只是判斷 autowireCandidate 屬性是否為 true,如果這個屬性為 false,就表示這個 Bean 不能被注入到其他 Bean 中,默認情況下該屬性為 true,如果想要設置這個屬性為 false,則可以在 @Bean 注解中設置,如下:
             
          @Bean(value = "b1",autowireCandidate = false)
          b1() {
              return new B();
          }

          從這個層面講,本文第一小節(jié)提出來的問題還有一種解決方案,就是把 autowireCandidate 屬性設置為 false。

          checkGenericTypeMatch 則主要是用來檢查類型是否匹配,這個就不去細看了。

          現(xiàn)在回到前面的 isAutowireCandidate 方法中,super.isAutowireCandidate 方法的匹配結果為 true,那么接下來就該 checkQualifiers 方法了。
          checkQualifiers 方法從名字上就能看出來,就是用來檢查 @Qualifier 注解的。
             
          protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
              if (ObjectUtils.isEmpty(annotationsToSearch)) {
                  return true;
              }
              SimpleTypeConverter typeConverter = new SimpleTypeConverter();
              for (Annotation annotation : annotationsToSearch) {
                  Class<? extends Annotation> type = annotation.annotationType();
                  boolean checkMeta = true;
                  boolean fallbackToMeta = false;
                  if (isQualifier(type)) {
                      if (!checkQualifier(bdHolder, annotation, typeConverter)) {
                          fallbackToMeta = true;
                      }
                      else {
                          checkMeta = false;
                      }
                  }
                  if (checkMeta) {
                      //...
              }
              return true;
          }
          這個方法會遍歷傳進來的注解,傳進來的注解數(shù)組是 A 中 B 屬性上的所有注解,以本文第一小節(jié)的案例為 1,這里是有兩個注解,分別是 @Autowired 和 @Qualifier。

          在這個方法中會去遍歷注解數(shù)組,判斷注解是否為 @Qualifier 類型的,如果是,則調用 checkQualifier 方法做進一步檢查。

          isQualifier 方法的邏輯很簡單:
             
          protected boolean isQualifier(Class<? extends Annotation> annotationType) {
              for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
                  if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
                      return true;
                  }
              }
              return false;
          }

          這個判斷是遍歷 qualifierTypes 集合,將集合中的注解類型挨個拿出來和傳入的參數(shù)進行比對,之所以是一個集合而不是直接拿 @Qualifier 注解做比對,是因為這個注解在 JSR-330 中也有一個實現(xiàn),如果項目用到了 JSR-330 的話,那么 qualifierTypes 集合中就有兩個注解。

          checkQualifier 方法算是整個 @Qualifier 處理最為核心的部分了:
             
          protected boolean checkQualifier(
                  BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
              Class<? extends Annotation> type = annotation.annotationType();
              RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
              AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
              if (qualifier == null) {
                  qualifier = bd.getQualifier(ClassUtils.getShortName(type));
              }
              if (qualifier == null) {
                  // First, check annotation on qualified element, if any
                  Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
                  // Then, check annotation on factory method, if applicable
                  if (targetAnnotation == null) {
                      targetAnnotation = getFactoryMethodAnnotation(bd, type);
                  }
                  if (targetAnnotation == null) {
                      RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
                      if (dbd != null) {
                          targetAnnotation = getFactoryMethodAnnotation(dbd, type);
                      }
                  }
                  if (targetAnnotation == null) {
                      // Look for matching annotation on the target class
                      if (getBeanFactory() != null) {
                          try {
                              Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
                              if (beanType != null) {
                                  targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
                              }
                          }
                          catch (NoSuchBeanDefinitionException ex) {
                              // Not the usual case - simply forget about the type check...
                          }
                      }
                      if (targetAnnotation == null && bd.hasBeanClass()) {
                          targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);
                      }
                  }
                  if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
                      return true;
                  }
              }
              Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
              if (attributes.isEmpty() && qualifier == null) {
                  // If no attributes, the qualifier must be present
                  return false;
              }
              for (Map.Entry<String, Object> entry : attributes.entrySet()) {
                  String attributeName = entry.getKey();
                  Object expectedValue = entry.getValue();
                  Object actualValue = null;
                  // Check qualifier first
                  if (qualifier != null) {
                      actualValue = qualifier.getAttribute(attributeName);
                  }
                  if (actualValue == null) {
                      // Fall back on bean definition attribute
                      actualValue = bd.getAttribute(attributeName);
                  }
                  if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
                          expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
                      // Fall back on bean name (or alias) match
                      continue;
                  }
                  if (actualValue == null && qualifier != null) {
                      // Fall back on default, but only if the qualifier is present
                      actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
                  }
                  if (actualValue != null) {
                      actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
                  }
                  if (!expectedValue.equals(actualValue)) {
                      return false;
                  }
              }
              return true;
          }

          來細細的說一下這個方法。

          1. 該方法首先獲取到注解的類型,一般情況下,這里拿到的注解就是 @Qualifier,如果使用了自定義注解的話,那么這里拿到的就是自定義注意,需要和小伙伴么強調一下,這里的注解是指 A 類中 B 屬性上的注解(并非提供 B 對象的 Java 方法上的注解)。


          2. 接下來會執(zhí)行 bd.getQualifier 方法,分別以第 1 步中拿到的注解全路徑(org.springframework.beans.factory.annotation.Qualifier)和短路徑(Qualifier)為參數(shù),去搜索看是否能夠獲取到一個 qualifier。那么什么時候能夠獲取到值呢?本文 1.4 小節(jié)的情況可以獲取到值,如:A 類有一個 B 屬性,B 屬性上有一個 @MyQualifier 注解,那么這里就會嘗試去 RootBeanDefinition 中也找到一個該注解,其實就是去看 XML 中是否有配置,XML 如果有配置,則直接進入第 8 步。
          3. 當然,對于 1.4 小節(jié)這種案例啟示我們日常開發(fā)中很少寫,所以一般情況下,經(jīng)過第 2 步之后,qualifier 變量還是為 null。那么接下來就調用 getQualifiedElementAnnotation 方法去查找注解。這個方法松哥感覺也是一個特別冷門的用法。該方法的本質實際上去查找當前 Bean 的定義中,是否存在 qualifiedElement,如果存在,則直接讀取 qualifiedElement 上的 @Qualifier 注解。松哥舉一個簡單例子,來給大家演示一下什么情況下,getQualifiedElementAnnotation 方法返回值不為 null。

          A 類和 1.1 小節(jié)的案例一樣,依然是通過 @Qualifier 注解去描述想要注入一個名為 b1 的 Bean,B 類如下:
             
          @Qualifier("b1")
          public class B {

          }

          然后在配置文件中加載:
             
          AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
          ctx.scan("org.javaboy.bean.p3");
          RootBeanDefinition bd = new RootBeanDefinition();
          bd.setBeanClass(B.class);
          bd.setQualifiedElement(B.class);
          ctx.registerBeanDefinition("b1", bd);
          ctx.refresh();

          大家看到,在這個配置文件中,我向 Spring 容器手動注冊了一個 BeanDefinition,并為這個 BeanDefinition 設置了 QualifiedElement 屬性。如此之后,在上面第 3 步的方法中,系統(tǒng)就會找到這個 QualifiedElement(即 B.class),然后讀取出來該類上面的注解,如果讀取到了,就直接進入到第 7 步。

          回到主線,我們來繼續(xù)看第 4 步。

          1. 一般來說,第 3 步這種寫法也很少見,所以基本上都會進入到第 4 步,現(xiàn)在是執(zhí)行 getFactoryMethodAnnotation 方法,這個方法就是去找到 JavaConfig 配置類中 b1() 方法上的 @Qualifier 注解,這種是比較常見的,一般在這一步,就可以拿到 targetAnnotation 了,獲取到 targetAnnotation 之后繼續(xù)執(zhí)行第 步。
          2. 當然,第 4 步也有可能沒有拿到 targetAnnotation,雖然這種情況比較少見,但是也和小伙伴們說一下,如果第 4 步?jīng)]有獲取到 targetAnnotation,那么接下來會調用 getResolvedDecoratedDefinition 方法獲取到一個裝飾之后的 BeanDefinition,然后繼續(xù)獲取從中獲取 targetAnnotation。這里所謂的 DecoratedDefinition 其實就是一個 BeanDefinitionHolder,這個里邊保存了一個 BeanDefinition,這種配置其實比較繁瑣,一般我們很少用,給小伙伴們簡單演示下,如下:
             
          AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
          RootBeanDefinition rbd = new RootBeanDefinition();
          GenericBeanDefinition bd = new GenericBeanDefinition();
          bd.setBeanClass(B.class);
          rbd.setDecoratedDefinition(new BeanDefinitionHolder(bd, "b99"));
          rbd.setBeanClass(B.class);
          ctx.registerBeanDefinition("b99", rbd);
          ctx.register(JavaConfig.class);
          ctx.refresh();

          這個日常開發(fā)中應該很少用,小伙伴們了解即可。

          回到住下,我么繼續(xù)來看第 6 步。

          1. 如果前面幾步還是沒有拿到 targetAnnotation,那么接下來就要去類上面查找,查看目標類上是否有 @Qualifier 注解了(如果我們在 B 類上添加了 @Qualifier 注解,就會在這里拿到),去類上面找注解的時候,分別先按照 bdHolder 中的名字找類型,按照類型找注解以及按照 db 類型找注解的方式去找。關于 targetAnnotation 的各種查找方式就上面這些,其實就是去找一下目標類上是否存在 @Qualifier 注解,存在的話,就拿到這個注解。
          2. 接下來,會有一個有意思的判斷,即,如果找到了 targetAnnotation,并且 targetAnnotation 還等于參數(shù)傳進來的 annotation,那么這不就是 1.2 小節(jié)的情況嗎?找到的 targetAnnotation 是 JavaConfig 類中 Bean 方法上的注解,參數(shù)傳進來的則是 A 類中 B 屬性上的注解,這倆相同的話,那沒錯了,這個 Bean 就正是需要的。
          3. 如果前面幾步都沒能 return,那么接下來就把傳入的參數(shù) annotation 中的屬性都提取出來,如果參數(shù)上沒有任何屬性,即相當于 A 類的 B 屬性上,雖然有 @Qualifier 注解,但是只有該注解,沒有任何屬性,那么顯然匹配不上,直接返回 false。
          4. 當?shù)?8 步成功拿到傳入?yún)?shù)的 annotation 屬性之后,接下來就遍歷這些屬性,獲取到屬性的 key 是 attributeName 以及 value 是 expectedValue,如果在前面第 2 步中拿到了 qualifier,那么就從 qualifier 中獲取對應的屬性值進行比較;如果 qualifier 中沒有獲取到 value,則從 BeanDefinition 的屬性去獲取也可以,但是很顯然這些一般都是沒有值的,拿不到。
          5. 如果還沒有拿到 actualValue,并且 attributeName 是 value,并且 expectedValue 是字符串類型,然后判斷 bdHolder.matchesName 中是否包含 expectedValue,這個判斷實質上就是查看 bdHolder 中定義的 Bean 名稱、別名等,是否和 expectedValue 相等,本文 1.1 小節(jié)中的案例,將在這里被比對到然后 continue,這里之所以不急著直接 return,是擔心后面還有其他屬性不滿足,如果后續(xù)其他屬性都滿足條件,那么直接在方法結尾處返回 true 即可。
          6. 如果前面還是沒能返回,并且 qualifier 不為空,那么就嘗試去獲取傳入注解的默認值,然后進行比較。
          以上就是 checkQualifier 方法完整的比較流程??偨Y一下,其實就兩步:

          • 先去找目標類上是否也存在 @Qualifier 注解,就是前面 7 步找 targetAnnotation 的過程,如果目標類上也存在該注解,直接做注解的比對即可,就不去管屬性了。
          • 如果沒有 targetAnnotation,即 @Qualifier 注解只出現(xiàn)在需求的一方(A 類屬性上才有),那么就把這個唯一的 @Qualifier 注解的屬性拿出來,分別跟 XML 配置、BeanDefinition 屬性、BeanName 等做比較,如果比對上了,就返回 true。

          checkQualifier 方法看完了,現(xiàn)在我們回到 checkQualifiers 方法中,如果 checkQualifier 返回 true,那么 checkMeta 就會為 false,這個表示是否檢查元注解,即如果 checkQualifier 比對失敗,就會遍歷當前注解的元注解,找到 @Qualifier,然后繼續(xù)調用 checkQualifier 方法進行比較,后續(xù)邏輯和前面基本一致,我就不贅述了。

          好了,經(jīng)過上面一整套流程后,findAutowireCandidates 方法所返回的 matchingBeans 就只有一個目標 Bean 了~



          3. 小結

          今天和小伙伴們梳理了一下 @Qualifier 注解的作用,老實說,松哥在源碼分析的過程中,也 GET 到 Spring 許多新的玩法,感興趣的小伙伴趕緊去試試吧~


          點擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動和交流,公眾號后臺回復“ 入群 ”即可加入我們的技術交流群,收獲更多的技術文章~

          - END -



          往期推薦



          社區(qū)精選|【動畫進階】神奇的 3D 磨砂玻璃透視效果


          社區(qū)精選|效率max:AI讀了源碼后再教我


          社區(qū)精選|透過源碼,捋清楚循環(huán)依賴到底是如何解決的!


          瀏覽 191
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲内射片 | 一本无码免费视频 | h片在线播放 | www国产夜插内射视频网站 | 美女操逼应用 |