【问题标题】:How to do conditional auto-wiring in Spring?如何在 Spring 中进行条件自动装配?
【发布时间】:2013-10-07 12:36:22
【问题描述】:

有没有人尝试根据条件将不同的 bean 自动连接到 Spring 管理的 bean 中?例如如果满足某些条件,则注入 A 类,否则注入 B?我在其中一个 Google 搜索结果中看到,使用 SpEL(Spring 表达式语言)是可能的,但找不到工作示例。

【问题讨论】:

  • 您也可以使用配置文件。

标签: spring


【解决方案1】:

有多种方法可以实现这一目标。大多数情况下,这取决于您要执行的调节。

工厂豆

您可以实现简单的工厂 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 创建 dependency bean 可能更好一点: p>

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

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",但该定义仍将在应用程序上下文中注册(并且在使用不合格的自动装配时会让您的生活更加困难)。您还可以通过 @Profile 注释将配置文件与基于 @Component 的 bean 一起使用。

检查ApplicationContextInitialier(或此example)以了解如何以编程方式激活配置文件(即基于您的条件)。

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 的内部工作原理。

BeanFactoryPostProcessors 可以读取和更改 bean 定义(例如,属性占位符 ${} 分辨率就是这样实现的)。

BeanPostProcessors 可以处理 bean 实例。可以检查新创建的 bean 并使用它(例如,@Scheduled 注释处理就是这样实现的)。

MergedBeanDefinitionPostProcessorbean 后处理器 的扩展,可以在实例化之前更改 bean 定义(@Autowired 注释处理就是这样实现的)。


2015 年 10 月更新

  • Spring 4 添加了一个新方法,如何通过 @Conditional 注释执行 conditional bean registration。这也值得检查。

  • 当然,通过 @ConditionalOn* 单独使用 Spring Boot 还有许多其他方法。

  • 另请注意,@Import@ComponentScan(及其 XML 对应项)都经过属性解析(即您可以使用 ${})。

【讨论】:

  • 好答案。我尝试几乎完全依赖配置参数,在尽可能少的情况下使用条件配置(最好为零,但有时这种技术需要切换数据库基础设施)。通常我们会保留一个包含主机名/数据库/电子邮件服务器等的“开发人员”属性文件,与“生产”属性分开......并将其与应用程序分开部署。
  • 嗨@Pavel,我想使用基于另一个类的属性的基类或子类。基本上这两个是策略类,我不想让它们自己成为单例,而是依靠 Spring 的单例 bean 将其中一个插入到我正在测试其属性的类中。不想尽可能多地引入特定于 Spring 的代码(Profile 看起来不错,但要以编程方式注入,Spring 代码似乎又是必要的)。您能否指导一下这种情况下的最佳方法?注释中的限定符和 SPEL 组合可以解决问题吗?
  • 您可以调用 bean 方法或使用 SPeL 检查 bean 属性。所以应该可以用#{myConditionBean.myProp == 'someValue' ? dependencyX : dependencyY}改变上面的SPeL示例。
  • 注入单例bean时会有副作用吗?我猜应该想法?
  • 不确定我是否理解您的担忧。你能在问题中添加更多细节吗?
【解决方案2】:

我有一个案例,我需要根据属性注入不同的 bean:“my.property”。就我而言,这个解决方案是成功的:

 <property name="name" ref="#{ ${my.property:false}==true ? 'bean1' : 'bean2' }"/>

我需要在 bean 名称周围添加撇号才能使其正常工作。

【讨论】:

    【解决方案3】:

    在您的 @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 bean,如果不存在,您将获得 stuff = null

    【讨论】:

      【解决方案4】:

      我想最简单的方法:

      @Autowired @Lazy
      protected A a;
      
      @Autowired @Lazy
      protected B b;
      
      void do(){
        if(...) { // any condition
           // use a
        } else {
           // use b
        }
      }
      

      如果你没有声明 nessassary bean,Spring 会在运行时抛出 NoSuchBeanDefinitionException

      【讨论】:

        猜你喜欢
        • 2014-01-20
        • 1970-01-01
        • 2013-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-09
        相关资源
        最近更新 更多