是否有人尝试根据条件将不同的bean自动连接到Spring管理的bean?对于例如如果满足某些条件,则注入A类,否则B?我在其中一个Google搜索结果中看到,SpEL(Spring Expression Language)可以实现,但无法找到一个有效的示例。
答案 0 :(得分:80)
有多种方法可以实现这一目标。大多数情况下,这取决于您想要执行的条件。
您可以实现简单的工厂bean来进行条件连线。这样的工厂bean可以包含复杂的条件逻辑:
public MyBeanFactoryBean implements FactoryBean<MyBean> {
// Using app context instead of bean references so that the unused
// dependency can be left uninitialized if it is lazily initialized
@Autowired
private ApplicationContext applicationContext;
public MyBean getObject() {
MyBean myBean = new MyBean();
if (true /* some condition */) {
myBean.setDependency(applicationContext.getBean(DependencyX.class));
} else {
myBean.setDependency(applicationContext.getBean(DependencyY.class));
}
return myBean;
}
// Implementation of isSingleton => false and getObjectType
}
如果您希望在应用程序上下文中只有一个这样的bean时使用 factory bean 来创建依赖bean ,那么可能会有更好的方法:
public MyDependencyFactoryBean implements FactoryBean<MyDependency> {
public MyDependency getObject() {
if (true /* some condition */) {
return new MyDependencyX();
} else {
return new MyDependencyY();
}
}
// Implementation of isSingleton => false and getObjectType
}
Spel有很多可能性。最常见的是基于system property的条件:
<bean class="com.example.MyBean">
<property name="dependency" value="#{systemProperties['foo'] == 'bar' ? dependencyX : dependencyY}" />
</bean>
您可以让属性占位符解析您的bean引用。依赖项名称可以是应用程序配置的一部分。
<bean class="com.example.MyBean">
<property name="dependency" ref="${dependencyName}" />
</bean>
通常,您要评估的条件意味着应该或不应该注册一整套bean。 Spring配置文件可用于此:
<!-- Default dependency which is referred by myBean -->
<bean id="dependency" class="com.example.DependencyX" />
<beans profile="myProfile">
<!-- Override `dependency` definition if myProfile is active -->
<bean id="dependency" class="com.example.DependencyY" />
</beans>
其他方法可以将bean定义标记为lazy-init="true"
,但定义仍将在应用程序上下文中注册(并且在使用不合格的自动装配时会使您的生活更加艰难)。您还可以通过@Component
注释使用基于@Profile
的bean的配置文件。
检查ApplicationContextInitialier
(或此example),了解如何以编程方式激活配置文件(即基于您的情况)。
这就是为什么基于Java的配置如此受欢迎的原因:
@Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
if (true /* some condition */) {
myBean.setDependency(dependencyX());
} else {
myBean.setDependency(dependencyY());
}
return myBean;
}
当然,您也可以在基于java的配置中使用或多或少的所有配置方法(通过@Profile
,@Value
或@Qualifier
+ @Autowired
)。
Spring提供了许多钩点和SPI,您可以在其中参与应用程序上下文生命周期。本节需要更多地了解Spring的内部工作原理。
BeanFactoryPostProcessor
可以读取和更改bean定义(例如,属性占位符${}
解析以这种方式实现。)
BeanPostProcessor
可以处理bean实例。可以检查新创建的bean并使用它(例如@Scheduled
注释处理以这种方式实现)。
MergedBeanDefinitionPostProcessor
是 bean post processor 的扩展,可以在实例化之前更改bean定义(@Autowired
注释处理以这种方式实现)。
2015年10月更新
Spring 4通过conditional bean registration注释添加了一种新方法@Conditional
。这也值得一试。
当然,通过@ConditionalOn*
单独使用Spring Boot还有很多其他方法。
另请注意,@Import
和@ComponentScan
(以及它们的XML副本)都会进行属性解析(即您可以使用${}
)。
答案 1 :(得分:1)
在您的@Configuration类中,声明要有条件地创建一个bean:
@Bean
@Conditional(CustomFeatureCondition.class)
public Stuff stuff() {
return new Stuff ();
}
使用带有 required = false 选项的@Autowire代替它:
@Component
@Setter(onMethod_ = @Autowired(required = false))
public class AnotherStuff {
private Stuff stuff;
// do stuff here
}
这样,如果上下文中存在填充豆,如果没有,则获取 stuff = null 。
答案 2 :(得分:1)
我想最简单的方法:
@Autowired @Lazy
protected A a;
@Autowired @Lazy
protected B b;
void do(){
if(...) { // any condition
// use a
} else {
// use b
}
}
如果不声明nessassary bean,Spring会在运行时抛出NoSuchBeanDefinitionException
答案 3 :(得分:0)
我遇到一种情况,需要根据属性“ my.property”注入不同的bean。就我而言,此解决方案是成功的:
<property name="name" ref="#{ ${my.property:false}==true ? 'bean1' : 'bean2' }"/>
我需要在bean名称周围添加撇号,以使其起作用。