欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

源碼分析Spring?中?@Qualifier?注解基本用法

 更新時(shí)間:2023年08月18日 15:02:54   作者:_江南一點(diǎn)雨  
這篇文章主要介紹了源碼分析Spring?中?@Qualifier?注解基本用法,在源碼分析的過程中,也?GET?到?Spring?許多新的玩法,感興趣的小伙伴趕緊去試試吧

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

1. 基本用法

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

1.1 指定 Bean 名稱

說(shuō)到 @Qualifier 注解,大家最容易想到的就是處理 Bean 注入的問題了,假設(shè)我有如下 Bean:

@Configuration
@ComponentScan
public class JavaConfig {
    @Bean(value = "b1")
    B b1() {
        return new B();
    }
    @Bean("b2")
    B b2() {
        return new B();
    }
}

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

現(xiàn)在在 A 中想要使用 B,如下:

@Component
public class A {
    @Autowired
    B b;
}

由于 @Autowired 注解是按照類型進(jìn)行 Bean 的注入的,此時(shí) Spring 容器中存在兩個(gè) B 實(shí)例,那么注入就會(huì)出錯(cuò),通過 @Qualifier 注解我們可以指定具體想要使用哪一個(gè) Bean:

@Component
public class A {
    @Autowired
    @Qualifier("b1")
    B b;
}

這樣就指定了在注入時(shí)使用 b1 這個(gè)對(duì)象了。

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

1.2 不指定 Bean 名稱

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

首先在 Bean 注入的時(shí)候,添加 @Qualifier 注解:

@Configuration
@ComponentScan
public class JavaConfig {
    @Bean(value = "b1")
    @Qualifier
    B b1() {
        return new B();
    }
    @Bean("b2")
    B b2() {
        return new B();
    }
}

大家看到,這里給 b1 添加了 @Qualifier 注解,但是未設(shè)置任何 value,然后在需要進(jìn)行 B 對(duì)象注入的地方,也添加 @Qualifier 注解:

@Component
public class A {
    @Autowired
    @Qualifier
    B b;
}

這樣也能解決問題。

1.3 自定義注解

例如我可以自定義一個(gè)注解,專門用來(lái)注入 b1 對(duì)象的注解,如下:

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Qualifier
public @interface B1Qualifier {
}

然后分別在注冊(cè) Bean 和使用 Bean 的時(shí)候,添加該注解:

@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;
}

這樣也是一個(gè)問題解決辦法。

1.4 XML 中的配置

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

假設(shè)我現(xiàn)在準(zhǔn)備向 Spring 容器中注入兩個(gè) 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>

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

現(xiàn)在,當(dāng)我想要在 A 中注入 B 的時(shí)候,可以按照如下方式來(lái):

@Component
public class A {
    @Autowired
    @Qualifier("b11")
    B b;
}

大家注意,這里 @Qualifier 注解的 value 是 b11,對(duì)應(yīng)了 qualifier 標(biāo)簽中的 value 屬性,表示將 id 為 b1 的 Bean 注入到 A 中的 b 屬性上。

如果沒有為 @Qualifier 設(shè)置 value,那么就會(huì)將 id 為 b2 的 Bean 注入進(jìn)來(lái),這個(gè)就相當(dāng)于我們前面 1.2 小節(jié)的案例。

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

如果想要自定義注解去匹配 qualifier 標(biāo)簽中提供的多種屬性,那么我們可以按照如下方式來(lái)進(jìn)行配置。

首先我們自定義注解,如下:

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Qualifier
public @interface MyQualifier {
    String name() default "";
}

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

接下來(lái)在 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>

接下來(lái)在 Bean 注入的時(shí)候,就可以使用 @MyQualifier 進(jìn)行匹配了:

@Component
public class A {
    @Autowired
    @MyQualifier(name = "b11")
    B b;
}

這個(gè)就表示匹配 name 屬性為 b11 的 Bean。

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

2. 源碼分析

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

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

Spring中最常用的注解之一@Autowired詳解

2.1 doResolveDependency

Spring中最常用的注解之一@Autowired詳解 的 3.3 小節(jié)中,我們提到,給 A 注入 B 的時(shí)候,會(huì)調(diào)用到 doResolveDependency 方法,我們?cè)賮?lái)看下該方法:

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;
		}
		//...
}

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

對(duì)于 @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;
}

在這個(gè)方法中,首先調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法查找出 B 這種類型的所有 beanName,對(duì)于本文一開始的案例來(lái)說(shuō),這里拿到兩個(gè) beanName,分別是 b1、b2,如下圖:

接下來(lái)就去遍歷 candidateNames,在遍歷的時(shí)候,有兩個(gè)判斷條件:

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

在 isAutowireCandidate 方法,又依次調(diào)了三次 isAutowireCandidate 方法,也就是說(shuō) isAutowireCandidate 方法一共調(diào)了四次之后,將會(huì)來(lái)到關(guān)鍵的 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;
}

在當(dāng)前方法中,首先會(huì)調(diào)用 super.isAutowireCandidate 方法去判斷這個(gè) Bean 將來(lái)是否被允許注入到其他 Bean 中:

@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);
}

這里又是兩件事,第一個(gè)是調(diào)用父類方法進(jìn)行判斷,這里單純只是判斷 autowireCandidate 屬性是否為 true,如果這個(gè)屬性為 false,就表示這個(gè) Bean 不能被注入到其他 Bean 中,默認(rèn)情況下該屬性為 true,如果想要設(shè)置這個(gè)屬性為 false,則可以在 @Bean 注解中設(shè)置,如下:

@Bean(value = "b1",autowireCandidate = false)
B b1() {
    return new B();
}

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

checkGenericTypeMatch 則主要是用來(lái)檢查類型是否匹配,這個(gè)就不去細(xì)看了。

現(xiàn)在回到前面的 isAutowireCandidate 方法中,super.isAutowireCandidate 方法的匹配結(jié)果為 true,那么接下來(lái)就該 checkQualifiers 方法了。

checkQualifiers 方法從名字上就能看出來(lái),就是用來(lái)檢查 @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;
}

這個(gè)方法會(huì)遍歷傳進(jìn)來(lái)的注解,傳進(jìn)來(lái)的注解數(shù)組是 A 中 B 屬性上的所有注解,以本文第一小節(jié)的案例為 1,這里是有兩個(gè)注解,分別是 @Autowired 和 @Qualifier。

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

isQualifier 方法的邏輯很簡(jiǎn)單:

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;
}

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

checkQualifier 方法算是整個(gè) @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;
}

來(lái)細(xì)細(xì)的說(shuō)一下這個(gè)方法。

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

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

@Qualifier("b1")
public class B {
}

然后在配置文件中加載:

AnnotationConfigApplicationContext ctx &#61; new AnnotationConfigApplicationContext();ctx.scan(&#34;org.javaboy.bean.p3&#34;);RootBeanDefinition bd &#61; new RootBeanDefinition();bd.setBeanClass(B.class);bd.setQualifiedElement(B.class);ctx.registerBeanDefinition(&#34;b1&#34;, bd);ctx.refresh();

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

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

  • 一般來(lái)說(shuō),第 3 步這種寫法也很少見,所以基本上都會(huì)進(jìn)入到第 4 步,現(xiàn)在是執(zhí)行 getFactoryMethodAnnotation 方法,這個(gè)方法就是去找到 JavaConfig 配置類中 b1() 方法上的 @Qualifier 注解,這種是比較常見的,一般在這一步,就可以拿到 targetAnnotation 了,獲取到 targetAnnotation 之后繼續(xù)執(zhí)行第 步。
  • 當(dāng)然,第 4 步也有可能沒有拿到 targetAnnotation,雖然這種情況比較少見,但是也和小伙伴們說(shuō)一下,如果第 4 步?jīng)]有獲取到 targetAnnotation,那么接下來(lái)會(huì)調(diào)用 getResolvedDecoratedDefinition 方法獲取到一個(gè)裝飾之后的 BeanDefinition,然后繼續(xù)獲取從中獲取 targetAnnotation。這里所謂的 DecoratedDefinition 其實(shí)就是一個(gè) BeanDefinitionHolder,這個(gè)里邊保存了一個(gè) BeanDefinition,這種配置其實(shí)比較繁瑣,一般我們很少用,給小伙伴們簡(jiǎn)單演示下,如下:
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();

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

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

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

以上就是 checkQualifier 方法完整的比較流程??偨Y(jié)一下,其實(shí)就兩步:

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

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

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

3. 小結(jié)

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

到此這篇關(guān)于源碼分析Spring 中 @Qualifier 注解基本用法的文章就介紹到這了,更多相關(guān)Spring @Qualifier 注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot?2.x整合Log4j2日志的詳細(xì)步驟

    SpringBoot?2.x整合Log4j2日志的詳細(xì)步驟

    log4j2優(yōu)越的性能其原因在于log4j2使用了LMAX,一個(gè)無(wú)鎖的線程間通信庫(kù)代替了,logback和log4j之前的隊(duì)列,并發(fā)性能大大提升,下面這篇文章主要給大家介紹了關(guān)于SpringBoot?2.x整合Log4j2日志的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • Java圖形界面GUI布局方式(小結(jié))

    Java圖形界面GUI布局方式(小結(jié))

    這篇文章主要介紹了Java圖形界面GUI布局方式(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用

    Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用

    這篇文章主要介紹了Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
    2022-01-01
  • Java測(cè)試框架Mockito的簡(jiǎn)明教程

    Java測(cè)試框架Mockito的簡(jiǎn)明教程

    這篇文章主要介紹了Java測(cè)試框架Mockito的簡(jiǎn)明教程,Mock 測(cè)試是單元測(cè)試的重要方法之一。本文介紹了基于 Java 語(yǔ)言的 Mock 測(cè)試框架 – Mockito 的使用。,需要的朋友可以參考下
    2019-06-06
  • java?環(huán)境配置(2023年詳細(xì)教程)

    java?環(huán)境配置(2023年詳細(xì)教程)

    這篇文章首先為了完善我的知識(shí)體系,今后一些軟件的安裝教程也可能會(huì)用到想寫一個(gè)更加詳細(xì)的,因?yàn)檫@并不僅僅是寫給?IT?行業(yè)的,其它行業(yè)可能也需要配置java環(huán)境
    2023-06-06
  • JAVA超級(jí)簡(jiǎn)單的爬蟲實(shí)例講解

    JAVA超級(jí)簡(jiǎn)單的爬蟲實(shí)例講解

    下面小編就為大家?guī)?lái)一篇JAVA超級(jí)簡(jiǎn)單的爬蟲實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-10-10
  • mybatis注解與xml常用語(yǔ)句匯總

    mybatis注解與xml常用語(yǔ)句匯總

    最近一直在用mybatis,由于需要使用到了動(dòng)態(tài)sql,遇到了一些問題,現(xiàn)在來(lái)總結(jié)一下,經(jīng)驗(yàn)教訓(xùn)。下面這篇文章主要給大家總結(jié)介紹了mybatis注解與xml常用語(yǔ)句的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-09-09
  • SpringBoot事件發(fā)布和監(jiān)聽詳解

    SpringBoot事件發(fā)布和監(jiān)聽詳解

    今天去官網(wǎng)查看spring boot資料時(shí),在特性中看見了系統(tǒng)的事件及監(jiān)聽章節(jié),所以下面這篇文章主要給大家介紹了關(guān)于SpringBoot事件發(fā)布和監(jiān)聽的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • 在SpringBoot中使用ResponseBodyAdvice自定義響應(yīng)的代碼實(shí)現(xiàn)

    在SpringBoot中使用ResponseBodyAdvice自定義響應(yīng)的代碼實(shí)現(xiàn)

    ResponseBodyAdvice是Spring Framework中的一個(gè)接口,允許您在將響應(yīng)寫入客戶端之前自定義響應(yīng),它通常與@ControllerAdvice注釋結(jié)合使用,以跨多個(gè)控制器將全局更改應(yīng)用于響應(yīng)主體,本文介紹了如何使用ResponseBodyAdvice的基本概述,需要的朋友可以參考下
    2024-12-12
  • java多線程中的異常處理機(jī)制簡(jiǎn)析

    java多線程中的異常處理機(jī)制簡(jiǎn)析

    在java多線程程序中,所有線程都不允許拋出未捕獲的checked exception,也就是說(shuō)各個(gè)線程需要自己把自己的checked exception處理掉,需要了解的朋友可以參考下
    2012-11-11

最新評(píng)論