如何为 spring 限定符实现 OR 逻辑?
How to implement OR logic for spring qualifiers?
我有以下配置:
@Qualifier1
@Qualifier2
@Bean
public MyBean bean1(){...}
@Qualifier2
@Qualifier3
@Bean
public MyBean bean2(){...}
@Qualifier1
@Qualifier2
@Qualifier3
@Bean
public MyBean bean3(){...}
@Qualifier3
@Bean
public MyBean bean4(){...}
@Qualifier1
@Bean
public MyBean bean5(){...}
而且是注入的地方:
@Qualifier2
@Qualifier3
@Autowired:
private List<MyBean> beans;
默认情况下 spring 对每个 @Qualifier
使用 AND
逻辑
所以bean2
和bean3
将被注入。
但是我想要 OR
那些东西的逻辑所以我希望 bean bean1
bean2
bean3
和 bean4
被注入
如何实现?
P.S.
@Qualifier
注释不可重复,所以我必须为每个注释创建元注释:
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Qualifier1 {
}
如果您使用标记接口而不是限定符会怎样?例如:
public class MyBean1 extends MyBean implements Marker1 {}
public class MyBean2 extends MyBean implements Marker2 {}
public class MyBean12 extends MyBean implements Marker1, Marker2 {}
然后使用这个:
@Bean
public MyBean1 myBean1() {
//...
}
@Bean
public MyBean2 myBean2() {
//...
}
@Bean
public MyBean12 myBean12() {
//...
}
还有这个:
@Autowired private List<Marker1> myBeans;
您将获得 myBean1
和 myBean12
个 bean 的列表。
为此:
@Autowired private List<Marker2> myBeans;
您将获得 myBean2
和 myBean12
个 bean 的列表。
这行得通吗?
更新我
自定义 FactoryBean
我实现了 TagsFactoryBean class and @Tags 注释,您可以使用它来解决您的任务(我希望 :))。
首先,用@Tags
注释标记你的bean:
@Tags({"greeting", "2letters"})
@Bean
public Supplier<String> hi() {
return () -> "hi";
}
@Tags({"parting", "2letters"})
@Bean
public Supplier<String> by() {
return () -> "by";
}
@Tags("greeting")
@Bean
public Supplier<String> hello() {
return () -> "hello";
}
@Tags("parting")
@Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
@Tags("other")
@Bean
public Supplier<String> other() {
return () -> "other";
}
然后准备TagsFactoryBean
:
@Bean
public TagsFactoryBean words() {
return TagsFactoryBean.<Supplier>builder()
.tags("greeting", "other")
.type(Supplier.class)
.generics(String.class)
.build();
}
这里 tags
是一个需要选择其 bean 的标签数组,type
是一个选择的 beans 类型,generics
是一个 beans 泛型类型的数组。最后一个参数是可选的,只有当你的 bean 是通用的时才应该使用它。
然后你可以使用它与@Qualifier
注释(否则Spring注入所有Supplier<String>
类型的bean):
@Autowired
@Qualifier("words")
private Map<String, Supplier<String>> beans;
Map beans
将包含三个 bean:hi
、hello
和 other
(它们的名称是 Map 的键,它们的实例是其值)。
您可以在 tests 中找到更多使用示例。
更新二
自定义 AutowireCandidateResolver
感谢@bhosleviraj , I implemented TaggedAutowireCandidateResolver 简化了自动装配所需 bean 的过程。只需用相同的标签标记你的 beans 和自动装配的集合,你就会将它们注入到集合中:
@Autowired
@Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;
@Configuration
static class Beans {
@Tags({"greeting", "2symbols", "even"})
@Bean
public Supplier<String> hi() {
return () -> "hi";
}
@Tags({"parting", "2symbols", "even"})
@Bean
public Supplier<String> by() {
return () -> "by";
}
@Tags({"greeting", "5symbols", "odd"})
@Bean
public Supplier<String> hello() {
return () -> "hello";
}
@Tags({"parting", "7symbols", "odd"})
@Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
@Tags({"other", "5symbols", "odd"})
@Bean
public Supplier<String> other() {
return () -> "other";
}
}
您不仅可以使用 Map 注入 bean,还可以使用其他 Collections。
要使其正常工作,您必须在应用程序中注册一个 CustomAutowireConfigurer
bean 并为其提供 TaggedAutowireCandidateResolver
:
@Configuration
public class AutowireConfig {
@Bean
public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
configurer.postProcessBeanFactory(beanFactory);
return configurer;
}
}
更多用法示例见此Test。
我想你不能通过使用注释来做到这一点。
我使用的是org.springframework.context.ApplicationContextAware
也许你需要写一些额外的代码,但这样你就可以解决你的问题。
我会像这样实现 class:
@Component
public class SpringContextAware implements ApplicationContextAware {
public static ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
public static synchronized ApplicationContext getCtx() {
return ctx;
}
}
然后在所有需要 OR 逻辑的 bean 中,您可以执行如下操作:
@Autowired
private SpringContextAware ctxAware;
@PostConstruct
public void init() {
//Here you can do your OR logic
ctxAware.getCtx().getBean("qualifier1") or ctxAware.getCtx().getBean("qualifier2")
}
这会解决您的问题吗?
安杰洛
答案需要深入了解Spring中如何实现自动装配解决方案,因此我们可以对其进行扩展。
我还没有想出任何解决方案,但我可以给你一些建议。
可能要扩展的候选者是 QualifierAnnotationAutowireCandidateResolver ,覆盖解析为合格 bean 的方法。并将自定义自动装配解析器传递给 bean 工厂。
您可以从此处克隆源代码并更正版本分支:
https://github.com/spring-projects/spring-framework
spring-beans 模块中有一个 CustomAutowireConfigurerTests,可能有助于您了解一些事情。
我有以下配置:
@Qualifier1
@Qualifier2
@Bean
public MyBean bean1(){...}
@Qualifier2
@Qualifier3
@Bean
public MyBean bean2(){...}
@Qualifier1
@Qualifier2
@Qualifier3
@Bean
public MyBean bean3(){...}
@Qualifier3
@Bean
public MyBean bean4(){...}
@Qualifier1
@Bean
public MyBean bean5(){...}
而且是注入的地方:
@Qualifier2
@Qualifier3
@Autowired:
private List<MyBean> beans;
默认情况下 spring 对每个 @Qualifier
AND
逻辑
所以bean2
和bean3
将被注入。
但是我想要 OR
那些东西的逻辑所以我希望 bean bean1
bean2
bean3
和 bean4
被注入
如何实现?
P.S.
@Qualifier
注释不可重复,所以我必须为每个注释创建元注释:
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Qualifier1 {
}
如果您使用标记接口而不是限定符会怎样?例如:
public class MyBean1 extends MyBean implements Marker1 {}
public class MyBean2 extends MyBean implements Marker2 {}
public class MyBean12 extends MyBean implements Marker1, Marker2 {}
然后使用这个:
@Bean
public MyBean1 myBean1() {
//...
}
@Bean
public MyBean2 myBean2() {
//...
}
@Bean
public MyBean12 myBean12() {
//...
}
还有这个:
@Autowired private List<Marker1> myBeans;
您将获得 myBean1
和 myBean12
个 bean 的列表。
为此:
@Autowired private List<Marker2> myBeans;
您将获得 myBean2
和 myBean12
个 bean 的列表。
这行得通吗?
更新我
自定义 FactoryBean
我实现了 TagsFactoryBean class and @Tags 注释,您可以使用它来解决您的任务(我希望 :))。
首先,用@Tags
注释标记你的bean:
@Tags({"greeting", "2letters"})
@Bean
public Supplier<String> hi() {
return () -> "hi";
}
@Tags({"parting", "2letters"})
@Bean
public Supplier<String> by() {
return () -> "by";
}
@Tags("greeting")
@Bean
public Supplier<String> hello() {
return () -> "hello";
}
@Tags("parting")
@Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
@Tags("other")
@Bean
public Supplier<String> other() {
return () -> "other";
}
然后准备TagsFactoryBean
:
@Bean
public TagsFactoryBean words() {
return TagsFactoryBean.<Supplier>builder()
.tags("greeting", "other")
.type(Supplier.class)
.generics(String.class)
.build();
}
这里 tags
是一个需要选择其 bean 的标签数组,type
是一个选择的 beans 类型,generics
是一个 beans 泛型类型的数组。最后一个参数是可选的,只有当你的 bean 是通用的时才应该使用它。
然后你可以使用它与@Qualifier
注释(否则Spring注入所有Supplier<String>
类型的bean):
@Autowired
@Qualifier("words")
private Map<String, Supplier<String>> beans;
Map beans
将包含三个 bean:hi
、hello
和 other
(它们的名称是 Map 的键,它们的实例是其值)。
您可以在 tests 中找到更多使用示例。
更新二
自定义 AutowireCandidateResolver
感谢@bhosleviraj
@Autowired
@Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;
@Configuration
static class Beans {
@Tags({"greeting", "2symbols", "even"})
@Bean
public Supplier<String> hi() {
return () -> "hi";
}
@Tags({"parting", "2symbols", "even"})
@Bean
public Supplier<String> by() {
return () -> "by";
}
@Tags({"greeting", "5symbols", "odd"})
@Bean
public Supplier<String> hello() {
return () -> "hello";
}
@Tags({"parting", "7symbols", "odd"})
@Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
@Tags({"other", "5symbols", "odd"})
@Bean
public Supplier<String> other() {
return () -> "other";
}
}
您不仅可以使用 Map 注入 bean,还可以使用其他 Collections。
要使其正常工作,您必须在应用程序中注册一个 CustomAutowireConfigurer
bean 并为其提供 TaggedAutowireCandidateResolver
:
@Configuration
public class AutowireConfig {
@Bean
public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
configurer.postProcessBeanFactory(beanFactory);
return configurer;
}
}
更多用法示例见此Test。
我想你不能通过使用注释来做到这一点。
我使用的是org.springframework.context.ApplicationContextAware
也许你需要写一些额外的代码,但这样你就可以解决你的问题。
我会像这样实现 class:
@Component
public class SpringContextAware implements ApplicationContextAware {
public static ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
public static synchronized ApplicationContext getCtx() {
return ctx;
}
}
然后在所有需要 OR 逻辑的 bean 中,您可以执行如下操作:
@Autowired
private SpringContextAware ctxAware;
@PostConstruct
public void init() {
//Here you can do your OR logic
ctxAware.getCtx().getBean("qualifier1") or ctxAware.getCtx().getBean("qualifier2")
}
这会解决您的问题吗?
安杰洛
答案需要深入了解Spring中如何实现自动装配解决方案,因此我们可以对其进行扩展。 我还没有想出任何解决方案,但我可以给你一些建议。 可能要扩展的候选者是 QualifierAnnotationAutowireCandidateResolver ,覆盖解析为合格 bean 的方法。并将自定义自动装配解析器传递给 bean 工厂。 您可以从此处克隆源代码并更正版本分支: https://github.com/spring-projects/spring-framework
spring-beans 模块中有一个 CustomAutowireConfigurerTests,可能有助于您了解一些事情。