有没有人尝试根据条件将不同的 bean 自动连接到 Spring 管理的 bean 中?例如,如果满足某些条件,则注入 A 类,否则注入 B?我在其中一个 Google 搜索结果中看到,使用 SpEL(Spring 表达式语言)是可能的,但找不到工作示例。
4 回答
有多种方法可以实现这一目标。大多数情况下,这取决于您要执行的调节。
工厂豆
您可以实现简单的工厂 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创建依赖 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 有很多可能性。最常见的是基于系统属性的条件:
<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。弹簧配置文件可用于此:
<!-- 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
基于 bean 的配置文件。@Profile
检查ApplicationContextInitialier
(或此示例)以查看如何以编程方式激活配置文件(即基于您的条件)。
Java 配置
这就是为什么基于 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
s 可以读取和更改 bean 定义(例如${}
,以这种方式实现属性占位符解析)。
BeanPostProcessor
s 可以处理 bean 实例。可以检查新创建的 bean 并使用它(例如,以@Scheduled
这种方式实现注释处理)。
MergedBeanDefinitionPostProcessor
是bean 后处理器的扩展,可以在实例化之前更改 bean 定义(@Autowired
注释处理以这种方式实现)。
2015 年 10 月更新
Spring 4 添加了一个新方法,如何通过注解进行条件 bean 注册。
@Conditional
这也值得检查。当然,仅通过 Spring Boot 的
@ConditionalOn*
.另请注意,
@Import
and@ComponentScan
(和它们的 XML 对应项)都经过属性解析(即您可以使用${}
)。
我有一个案例,我需要根据属性注入不同的 bean:“my.property”。就我而言,这个解决方案是成功的:
<property name="name" ref="#{ ${my.property:false}==true ? 'bean1' : 'bean2' }"/>
我需要在 bean 名称周围添加撇号以使其正常工作。
在您的 @Configuration 类中声明一个要有条件地创建的 bean:
@Bean
@Conditional(CustomFeatureCondition.class)
public Stuff stuff() {
return new Stuff ();
}
代替仅使用 @Autowire 它与required = false选项:
@Component
@Setter(onMethod_ = @Autowired(required = false))
public class AnotherStuff {
private Stuff stuff;
// do stuff here
}
这样,如果它存在于上下文中,您将获得Stuff bean ,如果不存在,您将获得stuff = null。
我想最简单的方法:
@Autowired @Lazy
protected A a;
@Autowired @Lazy
protected B b;
void do(){
if(...) { // any condition
// use a
} else {
// use b
}
}
如果你没有声明 nessassary bean,Spring 会在运行时抛出NoSuchBeanDefinitionException