社區(qū)精選|Spring 中 @Qualifier 注解還能這么用?
今天小編為大家?guī)淼氖巧鐓^(qū)作者 江南一點雨 的文章,讓我們一起來學習Spring 中 @Qualifier 注解。
1. 基本用法
1.1 指定 Bean 名稱
@Configuration
@ComponentScan
public class JavaConfig {
@Bean(value = "b1")
B b1() {
return new B();
}
@Bean("b2")
B b2() {
return new B();
}
}
@Component
public class A {
@Autowired
B b;
}
@Component
public class A {
@Autowired
@Qualifier("b1")
B b;
}
1.2 不指定 Bean 名稱
@Configuration
@ComponentScan
public class JavaConfig {
@Bean(value = "b1")
@Qualifier
B b1() {
return new B();
}
@Bean("b2")
B b2() {
return new B();
}
}
@Component
public class A {
@Autowired
@Qualifier
B b;
}
1.3 自定義注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Qualifier
public @interface B1Qualifier {
}
@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 中的配置
<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>
@Component
public class A {
@Autowired
@Qualifier("b11")
B b;
}
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Qualifier
public @interface MyQualifier {
String name() default "";
}
<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>
@Component
public class A {
@Autowired
@MyQualifier(name = "b11")
B b;
}
2. 源碼分析
2.1 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;
}
//...
}
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;
}

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

@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;
}
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
// If explicitly false, do not proceed with any other checks...
return false;
}
return checkGenericTypeMatch(bdHolder, descriptor);
}
@Bean(value = "b1",autowireCandidate = false)
B b1() {
return new B();
}
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;
}
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;
}
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;
}
-
該方法首先獲取到注解的類型,一般情況下,這里拿到的注解就是 @Qualifier,如果使用了自定義注解的話,那么這里拿到的就是自定義注意,需要和小伙伴么強調一下,這里的注解是指 A 類中 B 屬性上的注解(并非提供 B 對象的 Java 方法上的注解)。 -
接下來會執(zhí)行 bd.getQualifier 方法,分別以第 1 步中拿到的注解全路徑(org.springframework.beans.factory.annotation.Qualifier)和短路徑(Qualifier)為參數(shù),去搜索看是否能夠獲取到一個 qualifier。那么什么時候能夠獲取到值呢?本文 1.4 小節(jié)的情況可以獲取到值,如:A 類有一個 B 屬性,B 屬性上有一個 @MyQualifier 注解,那么這里就會嘗試去 RootBeanDefinition 中也找到一個該注解,其實就是去看 XML 中是否有配置,XML 如果有配置,則直接進入第 8 步。 -
當然,對于 1.4 小節(jié)這種案例啟示我們日常開發(fā)中很少寫,所以一般情況下,經(jīng)過第 2 步之后,qualifier 變量還是為 null。那么接下來就調用 getQualifiedElementAnnotation 方法去查找注解。這個方法松哥感覺也是一個特別冷門的用法。該方法的本質實際上去查找當前 Bean 的定義中,是否存在 qualifiedElement,如果存在,則直接讀取 qualifiedElement 上的 @Qualifier 注解。松哥舉一個簡單例子,來給大家演示一下什么情況下,getQualifiedElementAnnotation 方法返回值不為 null。
@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();
-
一般來說,第 3 步這種寫法也很少見,所以基本上都會進入到第 4 步,現(xiàn)在是執(zhí)行 getFactoryMethodAnnotation 方法,這個方法就是去找到 JavaConfig 配置類中 b1() 方法上的 @Qualifier 注解,這種是比較常見的,一般在這一步,就可以拿到 targetAnnotation 了,獲取到 targetAnnotation 之后繼續(xù)執(zhí)行第 步。 -
當然,第 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();
-
如果前面幾步還是沒有拿到 targetAnnotation,那么接下來就要去類上面查找,查看目標類上是否有 @Qualifier 注解了(如果我們在 B 類上添加了 @Qualifier 注解,就會在這里拿到),去類上面找注解的時候,分別先按照 bdHolder 中的名字找類型,按照類型找注解以及按照 db 類型找注解的方式去找。關于 targetAnnotation 的各種查找方式就上面這些,其實就是去找一下目標類上是否存在 @Qualifier 注解,存在的話,就拿到這個注解。 -
接下來,會有一個有意思的判斷,即,如果找到了 targetAnnotation,并且 targetAnnotation 還等于參數(shù)傳進來的 annotation,那么這不就是 1.2 小節(jié)的情況嗎?找到的 targetAnnotation 是 JavaConfig 類中 Bean 方法上的注解,參數(shù)傳進來的則是 A 類中 B 屬性上的注解,這倆相同的話,那沒錯了,這個 Bean 就正是需要的。 -
如果前面幾步都沒能 return,那么接下來就把傳入的參數(shù) annotation 中的屬性都提取出來,如果參數(shù)上沒有任何屬性,即相當于 A 類的 B 屬性上,雖然有 @Qualifier 注解,但是只有該注解,沒有任何屬性,那么顯然匹配不上,直接返回 false。 -
當?shù)?8 步成功拿到傳入?yún)?shù)的 annotation 屬性之后,接下來就遍歷這些屬性,獲取到屬性的 key 是 attributeName 以及 value 是 expectedValue,如果在前面第 2 步中拿到了 qualifier,那么就從 qualifier 中獲取對應的屬性值進行比較;如果 qualifier 中沒有獲取到 value,則從 BeanDefinition 的屬性去獲取也可以,但是很顯然這些一般都是沒有值的,拿不到。 -
如果還沒有拿到 actualValue,并且 attributeName 是 value,并且 expectedValue 是字符串類型,然后判斷 bdHolder.matchesName 中是否包含 expectedValue,這個判斷實質上就是查看 bdHolder 中定義的 Bean 名稱、別名等,是否和 expectedValue 相等,本文 1.1 小節(jié)中的案例,將在這里被比對到然后 continue,這里之所以不急著直接 return,是擔心后面還有其他屬性不滿足,如果后續(xù)其他屬性都滿足條件,那么直接在方法結尾處返回 true 即可。 -
如果前面還是沒能返回,并且 qualifier 不為空,那么就嘗試去獲取傳入注解的默認值,然后進行比較。
-
先去找目標類上是否也存在 @Qualifier 注解,就是前面 7 步找 targetAnnotation 的過程,如果目標類上也存在該注解,直接做注解的比對即可,就不去管屬性了。 -
如果沒有 targetAnnotation,即 @Qualifier 注解只出現(xiàn)在需求的一方(A 類屬性上才有),那么就把這個唯一的 @Qualifier 注解的屬性拿出來,分別跟 XML 配置、BeanDefinition 屬性、BeanName 等做比較,如果比對上了,就返回 true。
3. 小結
往期推薦
社區(qū)精選|【動畫進階】神奇的 3D 磨砂玻璃透視效果
社區(qū)精選|效率max:AI讀了源碼后再教我
社區(qū)精選|透過源碼,捋清楚循環(huán)依賴到底是如何解決的!
評論
圖片
表情
