首页 文章

如何在Spring中进行条件自动布线?

提问于
浏览
52

有没有人试图根据条件将不同的bean自动连接到Spring管理的bean?对于例如如果满足某些条件,则注入A类,否则B?我在其中一个Google搜索结果中看到,使用SpEL(Spring Expression Language)可以实现,但无法找到一个有效的示例 .

1 回答

  • 79

    有多种方法可以实现这一目标 . 大多数情况下,这取决于您想要执行的条件 .

    工厂bean

    您可以实现简单的工厂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

    使用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>
    

    Spring Profiles

    通常,您要评估的条件意味着应该或不应该注册一整套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内部工作的知识 .

    BeanFactoryPostProcessor s可以读取和更改bean定义(例如,属性占位符 ${} resolution以这种方式实现) .

    BeanPostProcessor s可以处理bean实例 . 可以检查新创建的bean并使用它(例如 @Scheduled 注释处理以这种方式实现) .

    MergedBeanDefinitionPostProcessor 是bean post processor的扩展,可以在实例化之前改变bean定义( @Autowired 注释处理以这种方式实现) .


    UPDATE Oct 2015

    • Spring 4添加了一个新方法conditional bean registration来自@Conditional注释 . 这也值得一试 .

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

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

相关问题