Как сделать условную автоматическую проводку в Spring?

Кто-нибудь пытался автоматизировать проводку различных beans в Spring -managed bean на основе условия? Напр. если выполнено какое-либо условие, введите класс A, иначе B? Я видел в одном из результатов поиска Google, что это возможно с помощью SpEL (Spring Язык выражения), но не удалось найти рабочий пример.

Ответ 1

Существует несколько способов достижения этого. В основном это зависит от условий, которые вы хотите выполнить.

Factory bean

Вы можете реализовать простой factory bean, чтобы выполнить условную проводку. Такой factory 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

}

Может быть, немного лучше подойти, если вы используете factory 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 существует много возможностей. Наиболее распространенными являются условия системного свойства:

<bean class="com.example.MyBean">
    <property name="dependency" value="#{systemProperties['foo'] == 'bar' ? dependencyX : dependencyY}" />
</bean>

Заполнитель свойств

У вас может быть свойство placeholder, разрешающее вашу ссылку bean. Имя зависимостей может быть частью конфигурации приложения.

<bean class="com.example.MyBean">
    <property name="dependency" ref="${dependencyName}" />
</bean>

Spring профили

Обычно условие, которое вы хотите оценить, означает, что весь набор beans должен или не должен быть зарегистрирован. Для этого можно использовать профили 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 на основе beans через аннотацию @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 может считывать и изменять определения bean (например, это означает, что разрешение заполнителя ${} реализовано таким образом).

BeanPostProcessor может обрабатывать экземпляры bean. Можно проверить только что созданный bean и играть с ним (например, @Scheduled обработка аннотации реализована таким образом).

MergedBeanDefinitionPostProcessor является расширением почтового процессора bean и может изменять определение bean непосредственно перед его созданием (@Autowired обработка аннотации реализована таким образом).


ОБНОВЛЕНИЕ октябрь 2015

  • Spring 4 добавлен новый метод, как сделать условную bean регистрацию через @Conditional аннотация. Это также стоит проверить.

  • Конечно, существует множество других способов использования Spring Boot через его @ConditionalOn*.

  • Также обратите внимание, что как @Import, так и @ComponentScan (и их XML-копии) подвергаются разрешению свойств (т.е. вы можете использовать ${}).

Ответ 2

У меня был случай, когда мне нужно было вводить разные bean-компоненты в зависимости от свойства: "my.property". В моем случае это решение было успешным:

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

Мне нужно было добавить апострофы вокруг имен бинов, чтобы они работали.