通过限定符从ApplicationContext获取bean

时间:2016-01-14 19:51:02

标签: spring

鉴于此代码:

public interface Service {}

@Component
@Qualifier("NotWanted")
public class NotWantedService implements Service {}

@Component
@Qualifier("Wanted")
public class WantedService implements Service {}

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(NotWantedService.class);
ctx.register(WantedService.class);
ctx.refresh()

我现在该怎么做:

ctx.getBean(Service.class)

只会获得@Qualifier("Wanted")而不是@Qualifier("NotWanted")的那个?我特别询问是否可以使用getBean来执行此操作,而不是注入类,然后使用该类作为一种代理。

5 个答案:

答案 0 :(得分:12)

您可以使用

BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), Service.class, "Wanted")

使用ctx.getBeanFactory()而非ctx本身非常重要,因为' qualifiedBeanOfType'方法只能为ConfigurableListenableBeanFactory解析限定符。

答案 1 :(得分:8)

通过ApplicationContext获取bean时,使用它不是@Qualifier注释的目的。但由于某些原因你需要这样或类似的功能,我建议一个解决方法。

创建@Wanted@NotWanted注释:

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
        ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Wanted {
}

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
            ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotWanted {
}

使用这些新注释注释您的bean类:

@Component
@NotWanted
public class NotWantedService implements Service {}

@Component
@Wanted
public class WantedService implements Service {}

然后你应该在有权访问ApplicationContext的地方添加2个方法:

ApplicationContext applicationContext;

private <T> Collection<T>  getBeansByTypeAndAnnotation(Class<T> clazz, Class<? extends Annotation> annotationType){
    Map<String, T> typedBeans = applicationContext.getBeansOfType(clazz);
    Map<String, Object> annotatedBeans = applicationContext.getBeansWithAnnotation(annotationType);
    typedBeans.keySet().retainAll(annotatedBeans.keySet());
    return typedBeans.values();
}

private <T> Optional<T> getBeanByTypeAndAnnotation(Class<T> clazz, Class<? extends Annotation> annotationType) {
    Collection<T> beans = getBeansByTypeAndAnnotation(clazz, annotationType);
    return beans.stream().findFirst();
}

现在您可以使用它们通过注释来获取bean或一个bean,并按以下方式输入:

Collection<Service> services = getBeansByTypeAndAnnotation(Service.class, Wanted.class);

Service service = getBeanByTypeAndAnnotation(Service.class, Wanted.class);

可能它不是处理问题的最佳方式。但是,由于我们无法通过限定符来获取ApplicationContext的bean,并打开了“开箱即用”,这就是其中一种方法。

答案 2 :(得分:2)

如果你想从上下文中获取bean而不是注入,更好的方法是在@Component注释中定义bean名称并从上下文中按名称获取它。在大多数情况下,@ Qualifier用于注射。

答案 3 :(得分:2)

就我而言,我有两个合格的一级豆,例如

@Configuration
public class AConfig {
    @Bean(name = "a")
    public Hello hello1() {
        return new Hello();
    }

    @Bean(name = "b")
    public Hello hello2() {
        return new Hello();
    }
}

然后我可以通过以下方式获取特定的豆

ApplicationContext context = SpringApplication.run(AutoConfiguration.class); 
var aHello = context.getBean("a", Hello.class); 
var bHello = context.getBean("b", Hello.class);

这是最简单的方法。或者,您可以执行以下相同操作:

var aHello = context.getBeansOfType(Hello.class).getBean("a");
var bHello = context.getBeansOfType(Hello.class).getBean("b");

或者您也可以按照@Dmitry Ovchinnikov的话做:

BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), Service.class, "Wanted")

在这种情况下,ctx.getBeanFactory()可以替换为context.getAutowireCapableBeanFactory()

答案 4 :(得分:1)

在Spring中执行此操作的最接近的规范方法是使用实​​用程序类BeanFactoryAnnotationUtils ...但遗憾的是这只能直接使用@Qualifier注释值参数(因此参数为字符串的原因)。

@Rozart is recommending是最好的方法,BeanFactoryAnnotationUtils应该是这样的。在有人登陆并确实想直接使用@Qualifier(以及随之附带的所有bean别名)的情况下,我只包含了上述答案。

我建议在春天提交一个功能请求(我会,但我认为他们可能会厌倦我嘲笑它们:-))。