Spring之@Qualifier注解的具體使用
1. 前言
當(dāng)我們注入的依賴存在多個(gè)候選者,我們得使用一些方法來(lái)篩選出唯一候選者,否則就會(huì)拋出異常
2. demo 演示
2.1 創(chuàng)建接口 Car,以及兩個(gè)實(shí)現(xiàn)其接口的子類
public interface Car { } @Component public class RedCar implements Car { } @Component public class WhiteCar implements Car { }
2.2 創(chuàng)建實(shí)體類 Person
@Component public class Person { @Autowired private Car car; }
2.3 創(chuàng)建配置類以及 Main 類
@ComponentScan("com.test.qualifier") public class AppConfig { } public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); Person bean = context.getBean(Person.class); System.out.println(bean); } }
2.4 運(yùn)行main方法
啟動(dòng)過(guò)程,拋出異常
2.5 解決方法
- 在 WhiteCar 或者 RedCar 所在的類上加 @Primary 注解
- 將 private Car car 改成 private Car redCar
- 使用 @Qualifier 注解
3. @Qualifier 注解源碼
從源碼中我們知道這個(gè)注解可以作用于屬性、方法、參數(shù)、類、注解上面
3.1 作用于屬性上
我們以 demo 演示代碼為前提,使用 @Qualifier 注解
3.1.1 改造 Person 類
@Component public class Person { @Autowired @Qualifier("redCar") private Car car; }
3.1.2 運(yùn)行main方法,查看運(yùn)行結(jié)果
3.2 作用于方法上
3.2.1 創(chuàng)建一個(gè)接口 Animal,以及兩個(gè)實(shí)現(xiàn)類
public interface Animal { } public class Dog implements Animal { } public class Cat implements Animal { }
3.2.2 創(chuàng)建配置類
@Configuration public class AnimalConfig { @Bean @Qualifier("mimi") public Cat cat(){ return new Cat(); } @Bean @Qualifier("wangcai") public Dog dog(){ return new Dog(); } }
3.2.3 改造 Person 類
@Component public class Person { @Autowired @Qualifier("redCar") private Car car; @Autowired @Qualifier("mimi") private Animal animal; }
3.2.4 運(yùn)行main方法,查看運(yùn)行結(jié)果
3.3 作用于類上
3.3.1 改造 Person 和 RedCar
@Component @Qualifier("car666") public class RedCar implements Car { } @Component public class Person { @Autowired @Qualifier("car666") private Car car; @Autowired @Qualifier("mimi") private Animal animal; }
3.3.2 運(yùn)行 main 方法,查看運(yùn)行結(jié)果
3.4 作用于參數(shù)上
3.4.1 改造 Person 類
@Component public class Person { @Autowired @Qualifier("car666") private Car car; private Animal animal; public Person(@Qualifier("wangcai") Animal animal) { this.animal = animal; } }
3.4.2 運(yùn)行 main 方法,查看運(yùn)行結(jié)果
3.5 作用于注解上
3.5.1 創(chuàng)建自定義注解 NestQualifier
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface NestQualifier { @AliasFor(annotation = Qualifier.class, attribute = "value") String value() default ""; }
3.5.2 自定義注解的使用
3.5.2.1 改造 Person 類
@Component public class Person { @Autowired @NestQualifier("redCar") private Car car; private Animal animal; public Person(@Qualifier("wangcai") Animal animal) { this.animal = animal; } }
3.5.2.2 改造 Person、RedCar 類
@Component public class Person { @Autowired @NestQualifier("car666") private Car car; private Animal animal; public Person(@Qualifier("wangcai") Animal animal) { this.animal = animal; } } @Component @NestQualifier("car666") public class RedCar implements Car { }
3.5.3 運(yùn)行main方法,查看運(yùn)行結(jié)果
3.6 小結(jié)
- 作用于方法上、作用于類上等于給 bean 添加了一個(gè) alias
- 作用于屬性上、作用于參數(shù)上時(shí)等于注入指定標(biāo)識(shí)符的 bean,這個(gè)標(biāo)識(shí)符既可以是 beanName,也可以是 alias
- 作用于注解上比較特殊,如果作用于方法上、作用于類上時(shí)用了包裝注解,作用于屬性上、作用于參數(shù)上也必須使用包裝注解,否則標(biāo)識(shí)符只能使用 beanName,使用 alias 會(huì)報(bào)錯(cuò)
4. 源碼解析
4.1 QualifierAnnotationAutowireCandidateResolver#checkQualifier
protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) { Class<? extends Annotation> type = annotation.annotationType(); RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition(); // 這里以@NestQualifier注解為例 // 判斷是否存在名稱為com.test.qualifier.annotations.NestQualifier的AutowireCandidateQualifier AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName()); if (qualifier == null) { // 判斷是否存在名稱為NestQualifier的AutowireCandidateQualifier qualifier = bd.getQualifier(ClassUtils.getShortName(type)); } if (qualifier == null) { // 判斷bd的qualifiedElement,是否存在@NestQualifier注解 Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type); // 判斷bd的factoryMethod,是否存在@NestQualifier注解 // PS:即@Bean標(biāo)注的方法上是否存在@NestQualifier注解 if (targetAnnotation == null) { targetAnnotation = getFactoryMethodAnnotation(bd, type); } // 判斷bd是否存在裝飾器bd,然后判斷裝飾器bd的factoryMethod,是否存在@NestQualifier注解 if (targetAnnotation == null) { RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd); if (dbd != null) { targetAnnotation = getFactoryMethodAnnotation(dbd, type); } } // 判斷bd的實(shí)際類型是否存在@NestQualifier注解 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... } } // 判斷bd的實(shí)際類型是否存在@NestQualifier注解,這里主要針對(duì)沒(méi)有傳入beanFactory的情況 if (targetAnnotation == null && bd.hasBeanClass()) { targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type); } } // 如果目標(biāo)注解等于方法傳入的注解,則返回true // 即屬性注入的value值和類上或者方法上的value值一致,則返回true 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; // 通過(guò)qualifier獲取actualValue if (qualifier != null) { actualValue = qualifier.getAttribute(attributeName); } // 通過(guò)bd獲取actualValue if (actualValue == null) { // Fall back on bean definition attribute actualValue = bd.getAttribute(attributeName); } // 即屬性注入的value值和beanName的值相等則,返回true if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) && expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) { // Fall back on bean name (or alias) match continue; } // actualValue等于null,qualifier不等于null,獲取value的默認(rèn)值 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()); } // 判斷@NestQualifier注解設(shè)置的值是否與默認(rèn)值相等 if (!expectedValue.equals(actualValue)) { return false; } } return true; }
4.2 通過(guò) BeanFactoryPostProcessor 來(lái)設(shè)置上述源碼中的一些值
@Component public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { ScannedGenericBeanDefinition scannedGenericBeanDefinition = (ScannedGenericBeanDefinition) registry.getBeanDefinition("redCar"); RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(scannedGenericBeanDefinition.getBeanClassName()); // 設(shè)置qualifiedElement beanDefinition.setQualifiedElement(RedCar.class); AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(NestQualifier.class); // 通過(guò)qualifier設(shè)置actualValue qualifier.setAttribute("value", "whiteCar"); beanDefinition.addQualifier(qualifier); // 通過(guò)bd設(shè)置actualValue beanDefinition.setAttribute("value", "redCar"); registry.registerBeanDefinition("redCar", beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { } }
PS : 被 @ComponentScan 注解掃描的帶有 @Component 注解的類會(huì)被解析成ScannedGenericBeanDefinition,但是 Spring 在實(shí)例化 bean 的時(shí)候會(huì)把所有 BeanDefinition 封裝成 RootBeanDefinition 處理,如果不提前改造 BeanDefinition 的話,RootBeanDefinition 屬性都是默認(rèn)值
到此這篇關(guān)于Spring之@Qualifier注解的具體使用的文章就介紹到這了,更多相關(guān)Spring @Qualifier注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot如何基于JWT實(shí)現(xiàn)單點(diǎn)登錄詳解
這篇文章主要介紹了spring boot如何基于JWT實(shí)現(xiàn)單點(diǎn)登錄詳解,用戶只需登錄一次就能夠在這兩個(gè)系統(tǒng)中進(jìn)行操作。很明顯這就是單點(diǎn)登錄(Single Sign-On)達(dá)到的效果,需要的朋友可以參考下2019-06-06Java實(shí)現(xiàn)讀取Jar文件屬性的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)讀取Jar文件屬性的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08springMVC如何將controller中Model數(shù)據(jù)傳遞到j(luò)sp頁(yè)面
本篇文章主要介紹了springMVC如何將controller中Model數(shù)據(jù)傳遞到j(luò)sp頁(yè)面,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07