认识 Spring 依赖注入中的 @Qualifer

前言

Spring 支持注入单一类型和集合类型的依赖,对于单一类型,如果按照类型进行注入,容器中存在多个相同类型的 bean 时,Spring 将抛出 NoUniqueBeanDefinitionException 异常。对于这种情况,我们可以选择将某一个 bean 设置为 primary,然而如果存在多个 primary 的 bean,Spring 仍将无法处理,这时便引出我们今天介绍的 @Qualifier,使用 @Qualifier 可以明确指出注入哪个 bean。

@Qualifier 注解的使用

@Qualifer 注解通常有两种用法。

  1. 依赖注入单一类型的 bean 时显式指出依赖的 bean 的名称,避免存在多个类型相同的 bean 而抛出异常。
  2. 依赖注入集合类型时为依赖进行分组。

注入单一类型 bean 的示例如下。

public class App {

    @Qualifier("bean1")	// ①
    @Autowired
    private String bean;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        context.register(App.class);

        context.refresh();

        System.out.println(context.getBean(App.class).bean);

        context.close();
    }

	// @Qualifier("bean1") ②
    @Bean
    public String bean1() {
        return "bean1";
    }

    @Bean
    public String bean2() {
        return "bean2";
    }

}

上述示例,容器中注册了两个类型为 String 的bean,在注入依赖时,使用 @Qualifier 指出需要注入 bean 的名称为 bean1,从而避免了抛出异常。注意此时等同于与在 bean1 上加入 @Qualifier("bean1") ,代码中位置①和位置②同时修改为 @Qualifier("bean") 也可以达到相同的目的。

使用 @Qualifier 为集合类型的依赖分组的示例如下。

public class App {

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

    }

    @Autowired
    private List<String> bean12;

    @Qualifier
    @Autowired
    private List<String> bean34;

    @MyQualifierGroup
    @Autowired
    private List<String> bean56;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(App.class);
        context.refresh();
        App app = context.getBean(App.class);
        System.out.println("bean12: "+app.bean12);
        System.out.println("bean34: "+app.bean34);
        System.out.println("bean56: "+app.bean56);
        context.close();
    }

    @Bean
    public String bean1() {
        return "bean1";
    }

    @Bean
    public String bean2() {
        return "bean2";
    }

    @Qualifier
    @Bean
    public String bean3() {
        return "bean3";
    }

    @Qualifier
    @Bean
    public String bean4() {
        return "bean4";
    }

    @MyQualifierGroup
    @Bean
    public String bean5() {
        return "bean5";
    }

    @MyQualifierGroup
    @Bean
    public String bean6() {
        return "bean6";
    }
}

上述示例中,在 Spring 容器中注册了6个 String 类型的 bean,其中 bean1,bean2 上没有加 @Qualifier ,bean3,bean4 上加了 @Qualifier 注解,bean5,bean6 上加了自定义的使用 @Qualifier 标注的注解 @MyQualifierGroup,同时在类型为 App 的 bean 中注入了三个 List<String> 类型的依赖,分别不加 @Qualifier,添加 @Qualifier,添加 @MyQualifierGroup 注解,打印结果如下所示。

bean12: [bean1, bean2, bean3, bean4, bean5, bean6]
bean34: [bean3, bean4, bean5, bean6]
bean56: [bean5, bean6]

不加 @Qualifier 注解,注入了所需类型的所有 bean,而加了 @Qualifier 注解后依赖上注解必须和我们指定的 @Qualifier 类型一致才会注入。

@Qualifier 实现简单分析

@Qualifier 作为注解,由处理注解的上下文进行处理,《掌握 Spring 必须知道的 BeanDefinition》 一文中曾提到,AnnotatedBeanDefinitionReader 会将注解信息读取为 BeanDefinition,AnnotatedBeanDefinitionReader 构造方法如下。

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		// 注册处理注解的处理器
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

实例化时,AnnotatedBeanDefinitionReader 会调用AnnotationConfigUtils#registerAnnotationConfigProcessors 向 Spring 注册一些处理注解的 BeanPostProcessor,跟踪源码如下。

	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				// 注册自动注入的候选项解析器
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}
		... 省略部分代码
	}

注册 BeanPostProcessor 时 Spring 会先注册自动注入的候选项解析器 ContextAnnotationAutowireCandidateResolver,重点就在这个解析器中。在文章《浅析 Spring 依赖解析实现》 中提到,Spring 解析依赖时会调用 DefaultListableBeanFactory#isAutowireCandidate 判断给定类型的 bean 是否为依赖的候选项,跟踪源码如下。

	protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
			DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {

		String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
		resolveBeanClass(mbd, beanDefinitionName);
		if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) {
			new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
		}
		BeanDefinitionHolder holder = (beanName.equals(beanDefinitionName) ?
				this.mergedBeanDefinitionHolders.computeIfAbsent(beanName,
						key -> new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName))) :
				new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)));
		// 解析给定的 bean 是否为自动注入的候选项
		return resolver.isAutowireCandidate(holder, descriptor);
	}

这里正是使用到了上面设置的 ContextAnnotationAutowireCandidateResolver,这个类会将 bean 上的 @Qualifier 和依赖描述符 DependencyDescriptor 中的 @Qualifier 信息进行匹配,从而对 bean 进行分组。

总结

@Qualifier 作为 Spring 官方提供的注解,在注入单一类型的 bean 时可以指定 bean 的名称,此时 @Autowired + @Qualifier = @Resource,同时在注入集合类型的 bean 时 @Qualifier 还可以为 bean 分组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏cool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值