spring · 17 9 月, 2021 0

spring中@Qualifier的两种使用方式

在spring中,提供了@Qualifier注解,帮助我们精确的注入需要的bean实例。这边文章主要介绍该注解在spring开发中的两种使用方式,能够帮助我们优雅的实现代码。

首先开始学习时,先查看下该注解的源码,以及注解方式,然后再以demo的方式实现。

@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.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @since 2.5
 * @see Autowired
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

    String value() default "";

}

通过源码注解可以得知, 该注解使用有两种场景:

  • 通过配合@Autowired注解实现精准bean注入
  • 自动注解,然后将自定义注解上加入@Qualifier注解,派生出新的注解

DEMO实现

接下来将通过代码的方式演示以上两种使用方式,下面我们通过定义配置类型,生成我们需要bean对象. 具体代码如下:

import org.spring.learn.eureka.client.annotation.MyQualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author xianglujun
 * @date 2021/9/17 11:06
 * @since
 */
@Configuration
public class MyQualifierConfiguration {

    public class BaseService {

    }

    @MyQualifier
    @Bean
    public BaseService baseService() {
        return new BaseService();
    }

    @Bean
    public BaseService noQualifierBaseService() {
        return new BaseService();
    }
}

在以上代码中,我自定义一个注解@MyQualifier, 该注解又被@Qualifier注解。因此@MyQualifier注解是@Qualifier注解的一个派生。具体代码如下:

/**
 * @author xianglujun
 * @date 2021/9/17 11:04
 * @since
 */
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface MyQualifier {

}

configuration的配置类中,我们创建了两个bean对象:

  • baseService的bean, 该bean是被自定义注解@MyQualifier修饰
  • noQualifierBaseService则是一个普通的bean对象,没有区别。

当以上类型配置完成之后,接下来我们验证这两个bean在使用上的差别,这里我们新建一个类型MyQualifierService,具体源码如下:

import java.util.List;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.spring.learn.eureka.client.annotation.MyQualifier;
import org.spring.learn.eureka.client.config.MyQualifierConfiguration.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

/**
 * @author xianglujun
 * @date 2021/9/17 11:09
 * @since
 */
@Slf4j
@Service
public class MyQualifierService {

    @Autowired
    private List<BaseService> baseServices;

    @Qualifier("baseService")
    @Autowired
    private BaseService onlyOneService;

    @MyQualifier
    @Autowired
    private BaseService baseService;

    @PostConstruct
    public void init() {
        log.info("baseService: {}", baseServices);
        log.info("baseService: {}", baseService);
    }
}

在这个类中包含了三个属性, 具体特点如下:

  • baseServices: 在spring容器中查找所有BaserService类型的bean对象
  • onlyOneService: 在spring容器中查找bean名称为baseService的实例
  • baseService: 则是查找类型为BaseService并且被@MyQualifier注解修饰的实例

我们将spring容器启动起来,并查看到日志内容如下:

2021-09-17 11:42:05.420  INFO 110960 --- [           main] o.s.l.e.c.service.MyQualifierService     : onlyOneService: org.spring.learn.eureka.client.config.MyQualifierConfiguration$BaseService@4375b013
2021-09-17 11:42:05.422  INFO 110960 --- [           main] o.s.l.e.c.service.MyQualifierService     : baseService: [org.spring.learn.eureka.client.config.MyQualifierConfiguration$BaseService@4375b013, org.spring.learn.eureka.client.config.MyQualifierConfiguration$BaseService@1cf0cacc]
2021-09-17 11:42:05.422  INFO 110960 --- [           main] o.s.l.e.c.service.MyQualifierService     : baseService: org.spring.learn.eureka.client.config.MyQualifierConfiguration$BaseService@4375b013

可以看到,程序能够正常的执行,并且能够正常的将所依赖的实体注入。并且跟上面我们对程序执行的推断也是正确的。

总结

当我们在平日开发中, 我们可以利用派生注解的方式,避免@Qualifier("name")这种硬代码注入bean. 所在在代码开发上,其实显得更加优雅一点吧。