精华内容
下载资源
问答
  • VolgaCTF Qualifier后端 VolgaCTF Qualifier平台后端。 对于开发人员 转码 $ npm run build 管理员实用程序 $ npm run cli 皮棉代码 $ npm run lint 执照 麻省理工学院@
  • 主要介绍了Spring实战之Qualifier注解用法,结合实例形式详细分析了spring Qualifier注解相关配置、定义与使用方法,需要的朋友可以参考下
  • 主要介绍了Spring @Primary和@Qualifier注解原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • NULL 博文链接:https://ysj5125094.iteye.com/blog/2054257
  • @Qualifier注解

    千次阅读 2019-02-12 14:52:44
    @Autowired是根据类型进行自动装配的。如果当spring上下文中存在不止一个UserDao类型的bean时,就会抛出...我们可以使用@Qualifier配合@Autowired来解决这些问题。如下: ①可能存在多个UserDao实...

    @Autowired是根据类型进行自动装配的。如果当spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题。如下:

    ①可能存在多个UserDao实例

     

    [java] view plain copy

    1. @Autowired   
    2. @Qualifier("userServiceImpl")   
    3. public IUserService userService;   

     

    或者

    [java] view plain copy

    1. @Autowired   
    2. public void setUserDao(@Qualifier("userDao") UserDao userDao) {   
    3.     this.userDao = userDao;   
    4. }  

     

    这样Spring会找到id为userServiceImpl和userDao的bean进行装配。

     

    ②可能不存在UserDao实例

     

    [java] view plain copy

    1. @Autowired(required = false)   
    2. public IUserService userService  

     

     

    三、@Qualifier:限定描述符,用于细粒度选择候选者;

    @Autowired默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常

    @Qualifier限定描述符除了能根据名字进行注入,更能进行更细粒度的控制如何选择候选者,具体使用方式如下:

     

     
    1. @Qualifier(value = "限定标识符")

    2. 字段、方法、参数

     

    (1)、根据基于XML配置中的<qualifier>标签指定的名字进行注入,使用如下方式指定名称:

     

    <qualifier  type="org.springframework.beans.factory.annotation.Qualifier"  value="限定标识符"/> 

    其中type属性可选,指定类型,默认就是Qualifier注解类,name就是给Bean候选者指定限定标识符,一个Bean定义中只允许指定类型不同的<qualifier>,如果有多个相同type后面指定的将覆盖前面的。

    1、准备测试Bean:

    DataSource.java

     

     
    1. package com.bean;

    2.  
    3. public interface DataSource {

    4.  
    5. public void connection();

    6. }

    MysqlDriveManagerDataSource.java

     

     
    1. package com.bean;

    2.  
    3. public class MysqlDriveManagerDataSource implements DataSource{

    4.  
    5. public void connection() {

    6. System.out.println("mysql database connecting...");

    7. }

    8.  
    9. }

    OracleDriveManagerDataSource.java

     

     
    1. package com.bean;

    2.  
    3. public class OracleDriveManagerDataSource implements DataSource{

    4.  
    5. public void connection() {

    6. System.out.println("oracle database connecting...");

    7. }

    8.  
    9. }

    TestBean.java

     

     
    1. package com.bean;

    2.  
    3. import org.springframework.beans.factory.annotation.Autowired;

    4. import org.springframework.beans.factory.annotation.Qualifier;

    5.  
    6.  
    7. public class TestBean {

    8.  
    9. private DataSource dataSource;

    10.  
    11. @Autowired

    12. public void initDataSource(@Qualifier("oracleDataSource") DataSource dataSource){

    13. this.dataSource = dataSource;

    14. }

    15.  
    16. public DataSource getDataSource() {

    17. return dataSource;

    18. }

    19. }

    其中TestBean.java中使用 @Qualifier还有一种方式

     

     
    1. @Autowired

    2. @Qualifier(value="oracleDataSource")

    3. public void initDataSource(DataSource dataSource){

    4. this.dataSource = dataSource;

    5. }

     

    2、在Spring配置文件 添加如下Bean配置:

     

    <bean id="testBean" class="com.bean.TestBean"/>

    我们使用@Qualifier("oracleDataSource")来指定候选Bean的限定标识符,我们需要在配置文件中使用<qualifier>标签来指定候选Bean的限定标识符“oracleDataSource”:

     

     
    1. <bean id="mysqlDataSourceBean" class="com.bean.MysqlDriveManagerDataSource">

    2. <qualifier value="mysqlDataSource"/>

    3. </bean>

    4.  
    5. <bean id="oracleDataSourceBean" class="com.bean.OracleDriveManagerDataSource">

    6. <qualifier value="oracleDataSource"/>

    7. </bean>

    3、测试方法如下:

     

     
    1. @Test

    2. public void autowiredTest(){

    3. TestBean bean = ctx.getBean("testBean", TestBean.class);

    4. DataSource dataSource = bean.getDataSource();

    5. if(dataSource instanceof MysqlDriveManagerDataSource){

    6. System.out.println("mysql");

    7. }else if(dataSource instanceof OracleDriveManagerDataSource){

    8. System.out.println("oracle");

    9. }

    10. dataSource.connection();

    11.  
    12. try{

    13. ctx.getBean("mysqlDataSource");

    14. }catch(Exception e){

    15. if(e instanceof NoSuchBeanDefinitionException){

    16. System.out.println("@Qualifier不能作为Bean的标识符");

    17. }

    18. e.printStackTrace();

    19. }

    20. }

    从测试可以看出使用<qualifier>标签指定的限定标识符只能被@Qualifier使用,不能作为Bean的标识符,如“ctx.getBean("mysqlDataSource")”是获取不到Bean的。

     

     

    (2)、缺省的根据Bean名字注入最基本方式,是在Bean上没有指定<qualifier>标签时一种容错机制,即缺省情况下使用Bean标识符注入,但如果你指定了<qualifier>标签将不会发生容错。

    1、准备测试Bean:

     

     
    1. package com.bean;

    2.  
    3. import org.springframework.beans.factory.annotation.Autowired;

    4. import org.springframework.beans.factory.annotation.Qualifier;

    5.  
    6.  
    7. public class TestBean {

    8.  
    9. private DataSource dataSource;

    10.  
    11. @Autowired

    12. @Qualifier(value="mysqlDataSourceBean")

    13. public void initDataSource(DataSource dataSource){

    14. this.dataSource = dataSource;

    15. }

    16.  
    17. public DataSource getDataSource() {

    18. return dataSource;

    19. }

    20. }

     

    2、在Spring配置文件 添加如下Bean配置:

     

     
    1. <bean id="mysqlDataSourceBean" class="com.bean.MysqlDriveManagerDataSource"/>

    2. <bean id="oracleDataSourceBean" class="com.bean.OracleDriveManagerDataSource"/>

    3、测试方法如下:

     

     
    1. @Test

    2. public void autowiredTest(){

    3. TestBean bean = ctx.getBean("testBean", TestBean.class);

    4. DataSource dataSource = bean.getDataSource();

    5. if(dataSource instanceof MysqlDriveManagerDataSource){

    6. System.out.println("mysql");

    7. }else if(dataSource instanceof OracleDriveManagerDataSource){

    8. System.out.println("oracle");

    9. }

    10. dataSource.connection();

    11.  
    12. }

    因为配置文件中并没有使用 @Qualifier标签 所以我们在bean中注入的时候是注入 bean  

    @Qualifier(value="mysqlDataSourceBean")

     

     

    (3)、扩展@Qualifier限定描述符注解(不带参数):对@Qualifier的扩展来提供细粒度选择候选者;
     

    具体使用方式就是自定义一个注解并使用@Qualifier注解其即可使用。

     

    首先让我们考虑这样一个问题,如果我们有两个数据源,分别为Mysql和Oracle,因此注入两者相关资源时就牵扯到数据库相关,如在DAO层注入SessionFactory时,当然可以采用前边介绍的方式,但为了简单和直观我们希望采用自定义注解方式。

     

    1、扩展@Qualifier限定描述符注解来分别表示Mysql和Oracle数据源

     

     
    1. package com.annotation;

    2.  
    3. import java.lang.annotation.ElementType;

    4. import java.lang.annotation.Retention;

    5. import java.lang.annotation.RetentionPolicy;

    6. import java.lang.annotation.Target;

    7.  
    8. import org.springframework.beans.factory.annotation.Qualifier;

    9.  
    10. @Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})

    11. @Retention(RetentionPolicy.RUNTIME)

    12. @Qualifier

    13. public @interface Mysql {

    14. }

     
    1. package com.annotation;

    2.  
    3. import java.lang.annotation.ElementType;

    4. import java.lang.annotation.Retention;

    5. import java.lang.annotation.RetentionPolicy;

    6. import java.lang.annotation.Target;

    7.  
    8. import org.springframework.beans.factory.annotation.Qualifier;

    9.  
    10. @Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})

    11. @Retention(RetentionPolicy.RUNTIME)

    12. @Qualifier

    13. public @interface Oracle {

    14. }

     

    2、准备测试Bean:

     

     
    1. package com.bean;

    2.  
    3. import org.springframework.beans.factory.annotation.Autowired;

    4.  
    5. import com.annotation.Mysql;

    6. import com.annotation.Oracle;

    7.  
    8. public class TestBean {

    9.  
    10. private DataSource mysqlDataSource;

    11.  
    12. private DataSource oracleDataSource;

    13.  
    14. @Autowired

    15. public void initDataSource(@Mysql DataSource mysqlDataSource, @Oracle DataSource oracleDataSource){

    16. this.mysqlDataSource = mysqlDataSource;

    17. this.oracleDataSource = oracleDataSource;

    18. }

    19.  
    20. public DataSource getMysqlDataSource() {

    21. return mysqlDataSource;

    22. }

    23.  
    24. public DataSource getOracleDataSource() {

    25. return oracleDataSource;

    26. }

    27. }

     

    3、在Spring配置文件 添加如下Bean配置:

    <bean id="testBean" class="com.bean.TestBean"/>

    4、在Spring修改定义的两个数据源:

     

     
    1. <bean id="mysqlDataSourceBean" class="com.bean.MysqlDriveManagerDataSource">

    2. <qualifier value="mysqlDataSource"/>

    3. <qualifier type="com.annotation.Mysql"/>

    4. </bean>

    5.  
    6. <bean id="oracleDataSourceBean" class="com.bean.OracleDriveManagerDataSource">

    7. <qualifier value="oracleDataSource"/>

    8. <qualifier type="com.annotation.Oracle"/>

    9. </bean>

    5、测试方法如下:

     

     
    1. @Test

    2. public void autowiredTest(){

    3. TestBean bean = ctx.getBean("testBean", TestBean.class);

    4. DataSource dataSource = bean.getMysqlDataSource();

    5. if(dataSource instanceof MysqlDriveManagerDataSource){

    6. System.out.println("mysql");

    7. }else if(dataSource instanceof OracleDriveManagerDataSource){

    8. System.out.println("oracle");

    9. }

    10. dataSource.connection();

    11. }

    测试也通过了,说明我们扩展的@Qualifier限定描述符注解也能很好工作。

     

     

    (3)、扩展@Qualifier限定描述符注解(带参数):对@Qualifier的扩展来提供细粒度选择候选者;

    前边演示了不带属性的注解,接下来演示一下带参数的注解:

    1、首先定义数据库类型:

     

     
    1. package com.enumBean;

    2.  
    3. public enum DataBase {

    4. ORACLE,MYSQL;

    5. }

    2、其次扩展@Qualifier限定描述符注解

     

     
    1. package com.annotation;

    2.  
    3. import java.lang.annotation.ElementType;

    4. import java.lang.annotation.Retention;

    5. import java.lang.annotation.RetentionPolicy;

    6. import java.lang.annotation.Target;

    7.  
    8. import org.springframework.beans.factory.annotation.Qualifier;

    9.  
    10. import com.enumBean.DataBase;

    11.  
    12. @Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})

    13. @Retention(RetentionPolicy.RUNTIME)

    14. @Qualifier

    15. public @interface DataSourceType {

    16. String ip(); //指定ip,用于多数据源情况

    17. DataBase database(); //指定数据库类型

    18. }

    3、准备测试Bean:

     

     
    1. package com.bean;

    2.  
    3. import org.springframework.beans.factory.annotation.Autowired;

    4.  
    5. import com.annotation.DataSourceType;

    6. import com.enumBean.DataBase;

    7.  
    8. public class TestBean {

    9.  
    10. private DataSource mysqlDataSource;

    11.  
    12. private DataSource oracleDataSource;

    13.  
    14. @Autowired

    15. public void initDataSource(@DataSourceType(ip="localhost",database=DataBase.MYSQL) DataSource mysqlDataSource,

    16. @DataSourceType(ip="localhost",database=DataBase.ORACLE) DataSource oracleDataSource){

    17. this.mysqlDataSource = mysqlDataSource;

    18. this.oracleDataSource = oracleDataSource;

    19. }

    20.  
    21. public DataSource getMysqlDataSource() {

    22. return mysqlDataSource;

    23. }

    24.  
    25. public DataSource getOracleDataSource() {

    26. return oracleDataSource;

    27. }

    28. }

     

    4、在Spring配置文件 添加如下Bean配置:

     

    <bean id="testBean" class="com.bean.TestBean"/>

    5、在Spring修改定义的两个数据源:

     

     
    1. <bean id="mysqlDataSourceBean" class="com.bean.MysqlDriveManagerDataSource">

    2. <qualifier value="mysqlDataSource"/>

    3. <qualifier type="com.annotation.DataSourceType">

    4. <attribute key="ip" value="localhost"/>

    5. <attribute key="database" value="MYSQL"/>

    6. </qualifier>

    7. </bean>

    8.  
    9. <bean id="oracleDataSourceBean" class="com.bean.OracleDriveManagerDataSource">

    10. <qualifier value="oracleDataSource"/>

    11. <qualifier type="com.annotation.DataSourceType">

    12. <attribute key="ip" value="localhost"/>

    13. <attribute key="database" value="ORACLE"/>

    14. </qualifier>

    15. </bean>

     

    6、测试方法如下:

     

     
    1. @Test

    2. public void autowiredTest(){

    3. TestBean bean = ctx.getBean("testBean", TestBean.class);

    4. DataSource dataSource = bean.getMysqlDataSource();

    5. if(dataSource instanceof MysqlDriveManagerDataSource){

    6. System.out.println("mysql");

    7. }else if(dataSource instanceof OracleDriveManagerDataSource){

    8. System.out.println("oracle");

    9. }

    10. dataSource.connection();

    11. }

     转载地址:https://blog.csdn.net/qq_38989725/article/details/76592791

    https://blog.csdn.net/lovin_fang/article/details/78537547

    展开全文
  • @Qualifier的作用和应用

    2021-04-29 00:54:46
    来源:blog.csdn.net/qq_30062181/article/details/107619529@Qualifier的作用这是官方的介绍This annotation may ...

    来源:blog.csdn.net/qq_30062181/article/details/107619529

    @Qualifier的作用

    这是官方的介绍

    This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.

    简单的理解就是:

    • 在使用@Autowire自动注入的时候,加上@Qualifier(“test”)可以指定注入哪个对象;

    • 可以作为筛选的限定符,我们在做自定义注解时可以在其定义上增加@Qualifier,用来筛选需要的对象。这个理解看下面的代码吧,不好解释。

    功能介绍

    首先是对(1)的理解。

    //我们定义了两个TestClass对象,分别是testClass1和testClass2
    //我们如果在另外一个对象中直接使用@Autowire去注入的话,spring肯定不知道使用哪个对象
    //会排除异常 required a single bean, but 2 were found
    @Configuration
    public class TestConfiguration {
       @Bean("testClass1")
       TestClass testClass1(){
           return new TestClass("TestClass1");
       }
       @Bean("testClass2")
       TestClass testClass2(){
           return new TestClass("TestClass2");
       }
    }
    

    下面是正常的引用

    @RestController
    public class TestController {
    
        //此时这两个注解的连用就类似 @Resource(name="testClass1")
        @Autowired
        @Qualifier("testClass1")
        private TestClass testClass;
    
        @GetMapping("/test")
        public Object test(){
            return testClassList;
        }
    
    }
    

    @Autowired和@Qualifier这两个注解的连用在这个位置就类似 @Resource(name=“testClass1”)

    对(2)的理解

    @Configuration
    public class TestConfiguration {
        //我们调整下在testClass1上增加@Qualifier注解
        @Qualifier
        @Bean("testClass1")
        TestClass testClass1(){
            return new TestClass("TestClass1");
        }
    
        @Bean("testClass2")
        TestClass testClass2(){
            return new TestClass("TestClass2");
        }
    }
    
    @RestController
    public class TestController {
        //我们这里使用一个list去接收testClass的对象
        @Autowired
        List<TestClass> testClassList= Collections.emptyList();
        
        @GetMapping("/test")
        public Object test(){
            return testClassList;
        }
    }
    

    我们调用得到的结果是

    [
         {
            "name": "TestClass1"
         },
        {
           "name": "TestClass2"
        }
    ]
    

    我们可以看到所有的testclass都获取到了。接下来我们修改下代码

    @RestController
    public class TestController {
    
        @Qualifier //我们在这增加注解
        @Autowired
        List<TestClass> testClassList= Collections.emptyList();
    
        @GetMapping("/test")
        public Object test(){
            return testClassList;
        }
    }
    

    和上面代码对比就是在接收参数上增加了@Qualifier注解,这样看是有什么区别,我们调用下,结果如下:

    [
         {
            "name": "TestClass1"
         }
    ]
    

    返回结果只剩下增加了@Qualifier注解的TestClass对象,这样我们就可以理解官方说的标记筛选是什么意思了。

    另外,@Qualifier注解是可以指定value的,这样我们可以通过values来分类筛选想要的对象了,这里不列举代码了,感兴趣的同学自己试试。

    推荐好文

    强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

    分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!

    能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮

    展开全文
  • Qualifier

    2018-09-16 00:19:00
    2019独角兽企业重金招聘... <qualifier value="platform" type="org.springframework.beans.factory.annotation.Qualifier"/> 转载于:https://my.oschina.net/u/3238650/blog/2051912
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
          <property name="dataSource" ref="dataSource"/>
          <qualifier value="platform" type="org.springframework.beans.factory.annotation.Qualifier"/>
      </bean>
    

    转载于:https://my.oschina.net/u/3238650/blog/2051912

    展开全文
  • 它可以被称为@Qualifier注解的实现类,专门用于解析此注解。至于@Qualifier有什么用如何用,这里就不啰嗦了,结合上面疑问进行原理分析如下: // @since 2.5 public class ...

    每篇一句

    罗斯:选秀状元可能有水货,但MVP绝对没有

    前言

    上篇文章(讲解@LoadBalanced负载均衡)的末尾,我抛出了一个很重要的问题,建议小伙伴自己深入思考一番;本文主要针对此问题,作出一个统一的答复和讲解。
    由于本人觉得这块知识点它属于Spring Framework的核心内容之一,非常的重要,因此单拎出来作专文讲述,希望对你有所帮助。

    背景案例

    说到@Qualifier这个注解大家并不陌生:它用于“精确匹配”Bean,一般用于同一类型的Bean有多个不同实例的case下,可通过此注解来做鉴别和匹配。
    本以为@Qualifier注解使用在属性上、类上用于鉴别就够了,直到我看到LoadBalancerAutoConfiguration里有这么应用:

    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();	
    

    它能把容器内所有RestTemplate类型并且标注有@LoadBalanced注解的Bean全注入进来
    这个用法让我非常的惊喜,它给我提供额外一条思路,让我的框架多了一种玩法。为了融汇贯通它,使用起来尽量避免不采坑,那就只能揭开它,从底层原理处理解它的用法了。


    QualifierAnnotationAutowireCandidateResolver详解

    它是依赖注入候选处理器接口AutowireCandidateResolver的实现类,继承自GenericTypeAwareAutowireCandidateResolver,所以此类是功能最全、最为强大的一个处理器(ContextAnnotationAutowireCandidateResolver除外~),Spring内默认使用它进行候选处理。

    它几乎可以被称为@Qualifier注解的"实现类",专门用于解析此注解。
    带着上面的疑问进行原理分析如下:

    // @since 2.5
    public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
    	// 是个List,可以知道它不仅仅只支持org.springframework.beans.factory.annotation.Qualifier
    	private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
    	private Class<? extends Annotation> valueAnnotationType = Value.class;
    
    
    	// 空构造:默认支持的是@Qualifier以及JSR330标准的@Qualifier
    	public QualifierAnnotationAutowireCandidateResolver() {
    		this.qualifierTypes.add(Qualifier.class);
    		try {
    			this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
    		} catch (ClassNotFoundException ex) {
    			// JSR-330 API not available - simply skip.
    		}
    	}
    	
    	// 非空构造:可自己额外指定注解类型
    	// 注意:如果通过构造函数指定qualifierType,上面两种就不支持了,因此不建议使用
    	// 而建议使用它提供的addQualifierType() 来添加~~~
    	public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) {
    	... // 省略add/set方法	
    
    	// 这是个最重要的接口方法~~~  判断所提供的Bean-->BeanDefinitionHolder 是否是候选的
    	// (返回true表示此Bean符合条件)
    	@Override
    	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    		// 1、先看父类:ean定义是否允许依赖注入、泛型类型是否匹配
    		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
    		// 2、若都满足就继续判断@Qualifier注解~~~~
    		if (match) {
    			// 3、看看标注的@Qualifier注解和候选Bean是否匹配~~~(本处的核心逻辑)
    			// descriptor 一般封装的是属性写方法的参数,即方法参数上的注解
    			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
    			// 4、若Field/方法参数匹配,会继续去看看参数所在的方法Method的情况
    			// 若是构造函数/返回void。 进一步校验标注在构造函数/方法上的@Qualifier限定符是否匹配
    		if (match) {
    				MethodParameter methodParam = descriptor.getMethodParameter();
    				// 若是Field,methodParam就是null  所以这里是需要判空的
    				if (methodParam != null) {
    					Method method = methodParam.getMethod();
    					// method == null表示构造函数 void.class表示方法返回void
    					if (method == null || void.class == method.getReturnType()) {
    						// 注意methodParam.getMethodAnnotations()方法是可能返回空的
    						// 毕竟构造方法/普通方法上不一定会标注@Qualifier等注解呀~~~~
    						// 同时警示我们:方法上的@Qualifier注解可不要乱标
    						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
    					}
    				}
    			}
    		}
    		return match;
    	}
    	...
    }
    

    在源码注释的地方,我按照步骤标出了它进行匹配的一个执行步骤逻辑。需要注意如下几点:

    • qualifierTypes是支持调用者自己指定的(默认只支持@Qualifier类型)
    • 只有类型匹配、Bean定义匹配、泛型匹配等全部Ok了,才会使用@Qualifier去更加精确的匹配
    • descriptor.getAnnotations()的逻辑是:
      - 如果DependencyDescriptor描述的是字段(Field),那就去字段里拿注解们
      - 若描述的是方法参数(MethodParameter),那就返回的是方法参数的注解
    • 步骤3的match = true表示Field/方法参数上的限定符是匹配的~

    说明:能走到isAutowireCandidate()方法里来,那它肯定是标注了@Autowired注解的(才能被AutowiredAnnotationBeanPostProcessor后置处理),所以descriptor.getAnnotations()返回的数组长度至少为1

    checkQualifiers()方法:
    QualifierAnnotationAutowireCandidateResolver:
    
    	// 将给定的限定符注释与候选bean定义匹配。命名中你发现:这里是负数形式,表示多个注解一起匹配
    	// 此处指的限定符,显然默认情况下只有@Qualifier注解
    	protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
    		// 很多人疑问为何没标注注解返回的还是true?
    		// 请参照上面我的解释:methodParam.getMethodAnnotations()方法是可能返回空的,so...可以理解了吧
    		if (ObjectUtils.isEmpty(annotationsToSearch)) {
    			return true;
    		}
    		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
    
    		// 遍历每个注解(一般有@Autowired+@Qualifier两个注解)
    		// 本文示例的两个注解:@Autowired+@LoadBalanced两个注解~~~(@LoadBalanced上标注有@Qualifier)
    		for (Annotation annotation : annotationsToSearch) {
    			Class<? extends Annotation> type = annotation.annotationType();
    			boolean checkMeta = true; // 是否去检查元注解
    			boolean fallbackToMeta = false;
    
    			// isQualifier方法逻辑见下面:是否是限定注解(默认的/开发自己指定的)
    			// 本文的org.springframework.cloud.client.loadbalancer.LoadBalanced是返回true的
    			if (isQualifier(type)) {
    				// checkQualifier:检查当前的注解限定符是否匹配
    				if (!checkQualifier(bdHolder, annotation, typeConverter)) {
    					fallbackToMeta = true; // 没匹配上。那就fallback到Meta去吧
    				} else {
    					checkMeta = false; // 匹配上了,就没必要校验元数据了喽~~~
    				}
    			}
    
    			// 开始检查元数据(如果上面匹配上了,就不需要检查元数据了)
    			// 比如说@Autowired注解/其它自定义的注解(反正就是未匹配上的),就会进来一个个检查元数据
    			// 什么时候会到checkMeta里来:如@A上标注有@Qualifier。@B上标注有@A。这个时候限定符是@B的话会fallback过来
    			if (checkMeta) {
    				boolean foundMeta = false;
    				// type.getAnnotations()结果为元注解们:@Documented、@Retention、@Target等等
    				for (Annotation metaAnn : type.getAnnotations()) {
    					Class<? extends Annotation> metaType = metaAnn.annotationType();
    					if (isQualifier(metaType)) {
    						foundMeta = true; // 只要进来了 就标注找到了,标记为true表示从元注解中找到了
    						// Only accept fallback match if @Qualifier annotation has a value...
    						// Otherwise it is just a marker for a custom qualifier annotation.
    						// fallback=true(是限定符但是没匹配上才为true)但没有valeu值
    						// 或者根本就没有匹配上,那不好意思,直接return false~
    						if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || !checkQualifier(bdHolder, metaAnn, typeConverter)) {
    							return false;
    						}
    					}
    				}
    				// fallbackToMeta =true你都没有找到匹配的,就返回false的
    				if (fallbackToMeta && !foundMeta) {
    					return false;
    				}
    			}
    		}
    		// 相当于:只有所有的注解都木有返回false,才会认为这个Bean是合法的~~~
    		return true;
    	}
    
    	// 判断一个类型是否是限定注解   qualifierTypes:表示我所有支持的限定符
    	// 本文的关键在于下面这个判断语句:类型就是限定符的类型 or @Qualifier标注在了此注解上(isAnnotationPresent)
    	protected boolean isQualifier(Class<? extends Annotation> annotationType) {
    		for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
    			// 类型就是限定符的类型 or @Qualifier标注在了此注解上(isAnnotationPresent)
    			if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
    				return true;
    			}
    		}
    		return false;
    	}
    

    checkQualifiers()方法它会检查标注的所有的注解(循环遍历一个个检查),规则如下:

    • 若是限定符注解(自己就是@Qualifier或者isAnnotationPresent),匹配上了,就继续看下一个注解
      - 也就说@Qualifier所标注的注解也算是限定符(isQualifier() = true)
    • 若是限定符注解但是没匹配上,那就fallback。继续看看标注在它身上的限定符注解(如果有)能否匹配上,若匹配上了也成
    • 若不是限定符注解,也是走fallback逻辑
    • 总之:若不是限定符注解直接忽略。若有多个限定符注解都生效,必须全部匹配上了,才算做最终匹配上。

    Tips:限定符不生效的效果不一定是注入失败,而是如果是单个的话还是注入成功的。只是若出现多个Bean它就无法起到区分的效果了,所以才会注入失败了~

    它的fallback策略最多只能再向上再找一个层级(多了就不行了)。例如上例子中使用@B标注也是能起到@Qualifier效果的,但是若再加一个@C层级,限定符就不生效了。

    注意:Class.isAnnotationPresent(Class<? extends Annotation> annotationClass)表示annotationClass是否标注在此类型上(此类型可以是任意Class类型)。
    此方法不具有传递性:比如注解A上标注有@Qualifier,注解B上标注有@A注解,那么你用此方法判断@B上是否有@Qualifier它是返回false的(即使都写了@Inherited注解,因为和它没关系)

    到这其实还是不能解释本文中为何@LoadBalanced参与了依赖注入,还得继续看精髓中的精髓checkQualifier()方法(方法名是单数,表示精确检查某一个单独的注解):

    QualifierAnnotationAutowireCandidateResolver:
    
    	// 检查某一个注解限定符,是否匹配当前的Bean
    	protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
    		// type:注解类型 bd:当前Bean的RootBeanDefinition 
    		Class<? extends Annotation> type = annotation.annotationType();		
    		RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
    	
    		// ========下面是匹配的关键步骤=========
    		// 1、Bean定义信息的qualifiers字段一般都无值了(XML时代的配置除外)
    		// 长名称不行再拿短名称去试了一把。显然此处 qualifier还是为null的
    		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
    			// 1、词方法是从bd标签里拿这个类型的注解声明,非XML配置时代此处targetAnnotation 为null
    			Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
    			// Then, check annotation on factory method, if applicable
    			// 2、若为null。去工厂方法里拿这个类型的注解。这方法里标注了两个注解@Bean和@LoadBalanced,所以此时targetAnnotation就不再为null了~~
    			if (targetAnnotation == null) {
    				targetAnnotation = getFactoryMethodAnnotation(bd, type);
    			}
    
    			// 若本类木有,还会去父类去找一趟
    			if (targetAnnotation == null) {
    				RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
    				if (dbd != null) {
    					targetAnnotation = getFactoryMethodAnnotation(dbd, type);
    				}
    			}
    
    
    			// 若xml、工厂方法、父里都还没找到此方法。那好家伙,回退到还去类本身上去看
    			// 也就是说,如果@LoadBalanced标注在RestTemplate上,也是阔仪的
    			if (targetAnnotation == null) {
    				// Look for matching annotation on the target class
    				...
    			}
    		
    			// 找到了,并且当且仅当就是这个注解的时候,就return true了~
    			// Tips:这里使用的是equals,所以即使目标的和Bean都标注了@Qualifier属性,value值相同才行哟~~~~
    			// 简单的说:只有value值相同,才会被选中的。否则这个Bean就是不符合条件的
    			if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
    				return true;
    			}
    		}
    
    		// 赞。若targetAnnotation还没找到,也就是还没匹配上。仍旧还不放弃,拿到当前这个注解的所有注解属性继续尝试匹配
    		Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
    		if (attributes.isEmpty() && qualifier == null) {
    			return false;
    		}
    		... // 详情不描述了。这就是为什么我们吧@Qualifier标注在某个类上面都能生效的原因 就是这里做了非常强大的兼容性~
    	}
    
    // =================它最重要的两个判断=================
    if (targetAnnotation != null && targetAnnotation.equals(annotation));
    
    // Fall back on bean name (or alias) match
    if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
    					expectedValue instanceof String && bdHolder.matchesName((String) expectedValue));
    
    

    checkQualifier()方法的实现,足以看到Spring作为一个优秀框架它对case的全面性,兼容性、灵活性的考虑还是很到位的。正因为Spring提供的强大的支持和灵活扩展,才给与了SpringBoot、SpringCloud在框架层面设计上更多可能性~




    @Qualifier高级使用

    @Autowired是根据类型进行自动装配的,当Spring容器内同一类型的Bean不止一个的时候,就需要借助@Qualifier来一起使用了。

    示例一:
    @Configuration
    public class WebMvcConfiguration {
    
        @Qualifier("person1")
        @Autowired
        public Person person;
    
        @Bean
        public Person person1() {
            return new Person("fsx01", 16);
        }
        @Bean
        public Person person2() {
            return new Person("fsx02", 18);
        }
    }
    

    单测代码如下(下同):

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(WebMvcConfiguration.class);
        WebMvcConfiguration bean = context.getBean(WebMvcConfiguration.class);
        // 打印字段的值
        System.out.println(bean.person);
    
    }
    

    运行后打印Person(name=fsx01, age=16),完全符合预期。这也是我们对@Qualifier注解最常规、最简单的使用。

    示例二:

    若你细心的话你可能注意到了@Qualifier注解它允许继承(@Inherited)、能标注在字段上、方法上、方法参数、类上、注解上
    因此它还能这么用:

    @Configuration
    public class WebMvcConfiguration {
    
        @MyAnno // 会把所有标注有此注解的Bean都收入囊中,请List装(因为会有多个)
        @Autowired
        public List<Person> person;
        
        @MyAnno
        @Bean
        public Person person1() {
            return new Person("fsx01", 16);
        }
        @MyAnno
        @Bean
        public Person person2() {
            return new Person("fsx02", 18);
        }
    
    	// 自定义注解:上面标注有@Qualifier注解
        @Target({FIELD, METHOD})
        @Retention(RetentionPolicy.RUNTIME)
        @Qualifier
        @interface MyAnno {
        }
    }
    

    运行单测,打印[Person(name=fsx01, age=16), Person(name=fsx02, age=18)],符合预期。

    实例三:

    若你不想自定义注解,直接使用@Qualifier注解分类注入也是可以的,如下案例:

    @Configuration
    public class WebMvcConfiguration {
        @Qualifier("person2")
        @Autowired
        public List<Person> person;
    
        @Qualifier("person2")
        @Bean
        public Person person1() {
            return new Person("fsx01", 16);
        }
    
        @Qualifier
        @Bean
        public Person person2() {
            return new Person("fsx02", 18);
        }
        @Qualifier
        @Bean
        public Person person3() {
            return new Person("fsx03", 20);
        }
    }
    

    运行的最终结果是:

    [Person(name=fsx01, age=16), Person(name=fsx02, age=18)]
    

    它把@Qualifier指定的value值相同的 或者 beanName(或者别名)相同的都注入进来了。这部分匹配代码为:

    checkQualifier方法:
    
    1、头上标注的注解完全equals(类型和value值都一样,算作匹配成功)
    	targetAnnotation != null && targetAnnotation.equals(annotation)
    	
    2、Fall back on bean name (or alias) match。若@Qualifier没匹配上,回退到BeanName的匹配,规则为:
       取头上注解的`value`属性(必须有此属性),如果beanName/alias能匹配上次名称,也算最终匹配成功了
       
    	actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
    	expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)
    
    

    备注:使用在类上、入参上的使用比较简单,此处就不做示范了。
    @Qualifier设计的细节可以看到,注解的value属性并不是必须的,所以它可以很好的使用在联合注解的场景。

    关于依赖注入和@Qualifier的使用亦需注意如下细节:

    1. @Autowired可不要写在Object类型的字段上去注入,因为容器内可以找到N多个会报错的。但是List<Object>是可以的(相当于把所有Bean都拿过来~)
    2. 可以利用@Qualifier这个高级特性,实现按需、按类别(不是类型)进行依赖注入,这种能力非常赞,给了框架二次开发设计者提供了更多的可能性

    如果说指定value是按照key进行限定/匹配,那么类似@LoadBalanced这种注解匹配可以理解成就是按照莫一类进行归类限定了,并且自由度也更高了。

    推荐阅读

    为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】

    总结

    本文介绍@Qualifier高级应用场景和案例,通过结合@LoadBalanced对此注解的使用,应该说是能给你打开了一个新的视角去看待@Qualifier,甚至看待Spring的依赖注入,这对后续的理解、自定义扩展/使用还是蛮有意义的。


    关注A哥

    AuthorA哥(YourBatman)
    个人站点www.yourbatman.cn
    E-mailyourbatman@qq.com
    微 信fsx641385712
    活跃平台
    公众号BAT的乌托邦(ID:BAT-utopia)
    知识星球BAT的乌托邦
    每日文章推荐每日文章推荐

    BAT的乌托邦

    展开全文
  • 今天带你了解一下Spring框架中的@Qualifier注解,它解决了哪些问题,以及如何使用它。我们还将了解它与@Primary注解的不同之处。更多的技术解析请访问felord.cn 2. 痛点 使用@Autowired注解是Spring依赖注入的绝好...
  • @Qualifier

    千次阅读 2019-06-13 19:33:11
    背景 我们知道,自动装配注入时,可以使用@Resource或者@Autowired注入bean。 但有时候仅仅一个bean_id还无法清晰明确出要注入的bean,因此可以...在class类和bean注入时,都加上@Qualifier(),来达到注入某个...
  • @Qualifier的作用

    万次阅读 多人点赞 2019-11-22 10:07:37
    Qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的; @Autowired默认按照类型匹配注入bean,如果有多个实现类,搭配@Qualifier(“实现类名称”)表明注入的是哪一个实现类的bean: 比如: ...
  • @Qualifier用处

    2020-02-02 22:10:42
    @Qualifier用处 ...
  • 三、@Qualifier:限定描述符,用于细粒度选择候选者; @Autowired默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常 @Qualifier限定描述符除了能根据名字...
  • Spring框架中的@Qualifier注解和@primary注解,它解决了哪些问题,以及如何使用它。我们还将了解它与@Primary注解的不同之处。 2. 痛点 使用@Autowired注解是Spring依赖注入的绝好方法。但是有些场景下仅仅靠这个...
  • } } @qualifier注解 @Qualifier限定哪个bean应该被自动注入。当Spring无法判断出哪个bean应该被注入时,@Qualifier注解有助于消除歧义bean的自动注入。参见下面的例子 public class Staff{ @Autowired private ...
  • 简述 虽然lombok 有很多缺点,但确实会减少很多代码。在Spring 中使用lombok 可以让注入... @Qualifier("curtaoRestClient") private final RestHighLevelClient restClient; private final ConstructorEsUtil constr
  • 首先明确一点:@Autowired @Qualifier 属于 Spring 定义的注解,@Resource 属于 JSR-250 规范定义的注解。以上注解 Spring 都支持。文章仅代表个人观点,如有不正之处,欢迎批评指正。 @Autowired 根据 bean ...
  • @Autowired @Qualifier 、 @Inject、@Resource 的区别
  • @Qualifier注解作用

    千次阅读 2020-10-21 00:28:46
    @Qualifier 注解作用就是为了给Bean打上一个标记,用来和其他的Bean做区分,代码示例: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021002048346.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5...
  • 使用@Qualifier可以分别为同样类型的Bean分别注入不同的依赖值。看个例子: Student.java: package com.sap; public class Student { private Integer age; private String name; public void setAge(Integer ...
  • @Qualifier注解的使用

    千次阅读 2020-08-29 10:13:59
    @Qualifier这个注解很简单。就是你一个接口有多个实现类。在Spring中使用了 @Autowired自动注入的时候不知道怎么选择是哪个具体的实现类了。 例如UserDao有2个实现类。 ** ** 如果你是Spring容器,给你一个UserDao...
  • Spring 5.x 源码之旅八十三Qualifier注解一@Qualifier注解例子-程序员选朋友Girl注解Boy注解Friend朋友Programmer程序员配置类测试类全女的/全男的男女都要 @Qualifier注解 说说Qualifier注解,这个注解可以用在注解...
  • 此时就可以在多个相同类型的组件上使用@Component("组件名")或者@Qualifier("组件名")等将其进行区分,然后在要自动注入的地方使用@Autowired+@Qualifier(“组件名”)`来指定注入哪个组件。 比如一个接口有多个实现...
  • @Autowire、@Resource、@Qualifier的区别一、简单说明@Autowire@Resource@Qualifier二、场景示例1、场景一(接口只有一个实现类)2、场景二(接口有多个实现类) 一、简单说明 这三个注解的作用都是用来注入依赖对象...
  • 1.@Qualifier修饰类的时候往往配合@Autowired,使用时候往往不能单独使用 点击查看@Autowired怎么使用 spring @Autowired_代码人生的博客-CSDN博客一.@Autowired解决的问题是不同接口实现类之间引用的问题,自动...
  • @Qualifier作用: 通过使用 @Qualifier 注解,我们可以消除需要注入哪个 bean 的问题。用来解决歧义。 在写配置类的时候,自己的@Qualifier注解老是不生效,报Error creating bean with name ...
  • 在SpringBoot整合Druid数据库连接池时候,配置主从库数据源,使用@Qualifier注解进行注入Bean时候,编译错误,提示: The annotation @Qualifier is disallowed for this location 错误原因: import导入的包...
  • Spring中的@Qualifier注解

    2020-07-04 11:45:45
    本文将带你来了解一下Spring中的@Qualifier注解,它解决了哪些问题,以及如何使用它。我们还将了解它与@Primary注解的不同之处。 使用@Autowired注解是Spring依赖注入的绝好方法。但是有些场景下仅仅靠这个注解不...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 79,121
精华内容 31,648
关键字:

qualifier