浅聊依赖注入中的@Qualifer

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

@Qualifier 注解的使用

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

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

注入单一类型 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 作为注解,由处理注解的上下文进行处理,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 解析依赖时会调用 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 进行分组。

 

参考:

 

posted @ 2022-01-13 13:08  残城碎梦  阅读(222)  评论(0编辑  收藏  举报