Spring 通过限定符从ApplicationContext获取bean

Spring 通过限定符从ApplicationContext获取bean,spring,Spring,鉴于此代码: public interface Service {} @Component @Qualifier("NotWanted") public class NotWantedService implements Service {} @Component @Qualifier("Wanted") public class WantedService implements Service {} AnnotationConfigApplicationContext ctx = new

鉴于此代码:

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(“想要的”)
的,而不会得到带有
@Qualifier(“不想要的”)
的?我特别想问的是,是否可以使用
getBean
,而不是注入到类中,然后将该类用作代理。

当通过ApplicationContext获取Bean时,使用
@Qualifier
注释并不是为了使用它。但是,由于某些原因,您需要此类或类似的功能,我建议您采取一种变通方法

创建
@Wanted
@NotWanted
注释:

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

使用以下新注释注释bean类:

@Component
@NotWanted
public class NotWantedService implements Service {}

然后,您应该在可以访问
ApplicationContext
的地方添加两个方法:

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();
}

也许这不是解决这个问题的最好办法。但是,由于我们无法通过限定符和输入“开箱即用”从
ApplicationContext
获取bean,这是实现这一点的方法之一。

如果您希望从上下文而不是注入获取bean,那么最好在@Component annotation中定义bean名称,并从上下文按名称获取。在大多数情况下,@Qualifier用于注入。

在Spring中实现这一点的最接近标准的方法是使用实用程序类。。。但遗憾的是,这只适用于
@Qualifier
注释值参数(因此参数是字符串)

最好的方法和类似的方法应该是在
BeanFactoryAnnotationUtils
中。我只在有人登陆这里并且确实想直接使用
@Qualifier
的情况下(以及随之而来的所有bean别名)提供了上述答案

我建议在春季提交一个功能请求(我会的,但我想他们可能厌倦了我对他们的窃听:-)。

你可以使用

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

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

在我的例子中,我有一个类的两个限定bean,例如

@配置
公共类乌头图{
@Bean(name=“a”)
公共Hello hello1(){
返回新的Hello();
}
@Bean(name=“b”)
公共Hello hello2(){
返回新的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,“通缉”)

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

谢谢@Rozart-我已经试过了。不幸的是,这并没有考虑到
@限定符
。它采用bean的名称,该名称由
@bean(name=“abc”)
指定,但不相同。这与
@bean(name=“abc”)
相同,但语义不同。不能有两个同名bean,但可以有多个同名bean。也就是说,我想要10种不同的服务,都符合“通缉”的要求,但根本不叫“通缉”。我已经更新了答案。也许这会对你有所帮助。:)谢谢你,罗扎特。您能否澄清一下:“当通过ApplicationContext获取bean时,@Qualifier注释的目的不是使用它。”?它目前可能不受支持,但为什么逻辑上会不一致呢?这正是我所说的“这不是@Qualifier的目的”——目前不受支持。从逻辑上讲,它是一致的,但尚未得到支持。我希望我的回答对你有帮助既然您使用的是name常量,也就是
ctx.getBean(“通缉”)
,为什么不按名称获取bean呢?@aux假设您有50个位置
ctx.getBean(“service1”)
,现在您想将其更改为
ctx.getBean(“service2”)
。这是50个变化。更改限定符只会更改为2个bean定义(
service1
service2
)。还有其他一些情况——比如说我想得到多个
服务
实例,这些实例都是
想要的
。它们不能都有相同的bean名称。好的,我明白了。那么,引入您自己的“注册表”bean怎么样?它保存对bean的引用,并用于通过不同参数进行查找,比如SpringDataREST中的
存储库
?还是包装豆?@参见问题的最后一句。我想知道我是否想取消间接性。换句话说,我有一个有效的解决方案,试图看看是否有更好的解决方案(阅读:更少的代码)。当我使用
application.run()
来获取
ctx
?@UnixAgain你试过
newannotationconfigapplicationcontext()吗
ctx的实例
?@UnixAgain使用applicationContext.getAutowireCapableBeanFactory()@AbhishekChatterjee谢谢。我终于找到了原因。我的bean类是作为一个单例实现的,因此实例是相同的。更多关于我的解决方案的详细信息将在这个问题下发布。@TuGordoBello谢谢。我终于找到了原因。我的bean类是作为一个单例实现的,因此实例是相同的。我的解决方案的更多详细信息发布在这个问题下面。
context.get()
方法给了我一个
BeanDefinitionOverrideeException
@bart kosmala抱歉,这是
context.getBean()
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();
}
Collection<Service> services = getBeansByTypeAndAnnotation(Service.class, Wanted.class);
Service service = getBeanByTypeAndAnnotation(Service.class, Wanted.class);
BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), Service.class, "Wanted")