Как добиться условного импорта ресурсов в контексте Spring XML?


то, что я хотел бы достичь, - это возможность "динамически" (т. е. на основе свойства, определенного в файле конфигурации) включить/отключить импорт дочернего контекста XML Spring.

Я представляю себе что-то вроде:

<import condition="some.property.name" resource="some-context.xml"/>

где свойство разрешается (в логическое значение) и когда true импортируется контекст, в противном случае это не так.

некоторые из моих исследований до сих пор:

  • написание пользовательского NamespaceHandler (и связанные классов), поэтому я могу зарегистрировать свой собственный пользовательский элемент в своем собственном пространстве имен. Например: <myns:import condition="some.property.name" resource="some-context.xml"/>

    проблема с этим подходом заключается в том, что я не хочу реплицировать всю логику импорта ресурсов из Spring, и для меня не очевидно, что мне нужно делегировать для этого.

  • переопределение DefaultBeanDefinitionDocumentReader чтобы расширить поведение разбора и интерпретации элемента "импорт" (что происходит там в importBeanDefinitionResource метод). Однако я не уверен, где я могу зарегистрироваться это расширение.
8   52  

8 ответа:

теперь это полностью возможно, используя пружину 4.

в главном файле содержимого приложения

<bean class="com.example.MyConditionalConfiguration"/>

и MyConditionalConfiguration выглядит как

@Configuration
@Conditional(MyConditionalConfiguration.Condition.class)
@ImportResource("/com/example/context-fragment.xml")
public class MyConditionalConfiguration {
    static class Condition implements ConfigurationCondition {
         @Override
         public ConfigurationPhase getConfigurationPhase() {
             return ConfigurationPhase.PARSE_CONFIGURATION;
         }
         @Override
         public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
             // only load context-fragment.xml if the system property is defined
             return System.getProperty("com.example.context-fragment") != null;
         }
    }
}

и затем, наконец, вы помещаете определения bean, которые вы хотите включить в /com/example/context-fragment.xml

посмотреть JavaDoc для @Conditional

самое близкое вы можете получить используя стандартные компоненты весны является следующим:

<import resource="Whatever-${yyzzy}.xml"/>

здесь ${xyzzy} интерполирует свойство из системных свойств. (Я использую хакерскую пользовательскую версию класса context loader, которая добавляет свойства из других мест в объект свойств системы перед началом процесса загрузки.)

но вы также можете уйти с импортом много ненужного хлама ... и используйте различные трюки, чтобы только вызвать необходимые бобы для создания экземпляра. Эти трюки включают в себя:

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

С Весны 3.1.X вы можете использовать профили зерен для достижения условного импорта ресурсов и создания экземпляра компонента. Это конечно не поможет, если вы используете более раннюю версию :)

Как упоминалось ранее, это можно легко сделать с помощью профилей, Если вы используете Spring 3.1+

<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it's put at the end of the configuration file -->
<!-- so no bean definitions after that -->
<beans profile="default">
    <import resource="classpath:default.xml" />
</beans>
<!-- some other profile -->
<beans profile="otherProfile">
    <import resource="classpath:other-profile.xml" />
</beans>

otherProfile можно легко активировать, например, с помощью

mvn install -Dspring.profiles.active=otherProfile

если вы используете разные профили в тестах, просто добавьте -DforkMode=never чтобы убедиться, что тесты будут выполняться внутри той же виртуальной машины, поэтому param spring.profiles.active не будут потеряны,

для записи Роберт Мэлдон объясняет, как выполнить условное определение бобов в этом посте:http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html это немного долго, чтобы скопировать его здесь (кроме того, я не думаю, что я должен скопировать-вставить свою статью в любом случае).

конечным результатом этого подхода, адаптированного для вашего примера, является:

<condbean:cond test="${some.property.name}">
  <import resource="some-context.xml"/>
</condbean:cond>

это, конечно, не так просто, как решение Стивена C, но это гораздо больше мощно.

другой вариант-сделать так, чтобы приложение загрузить модули-конфигурация.xml-файл, который находится в папке /conf, и отредактируйте его на этапе установки/конфигурации, чтобы раскомментировать модули, которые вы хотите загрузить.

Это решение, которое я использую с веб-приложением, которое служит контейнером для различных модулей интеграции. Веб-приложение распространяется со всеми различными интеграционными модулями. А модули-конфиг.XML-файле помещается в папку продукта Tomcat /Conf в директории conf добавляется classpath (via catalina.свойства/общие.собственность погрузчик). Мое веб-приложение webapp-config.xml имеет <import resource="classpath:/modules-config.xml"/> чтобы загрузить его.

еще один, чтобы рассмотреть для весны 3.0:

 <alias name="Whatever" alias=""Whatever-${yyzzy}" />

здесь ${xyzzy} интерполирует свойство из системных свойств.

вы можете переопределить contextInitialized (javax.сервлет.ServletContextEvent event) в вашем собственном ContextLoaderListener и установите необходимое системное свойство перед super.contextInitialized (событие) называется так

package com.mypackage;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
    public void contextInitialized(javax.servlet.ServletContextEvent event) {
        System.setProperty("xyz", "import-file-name.xml");
        super.contextInitialized(event);
    }
}

и чем заменить ContextLoaderListener на MyContextLoaderListener в вашем интернете.xml

<listener>
    <listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>

Теперь вы можете использовать в вашем весны.xml

<import resource="${xyz}" /> 

Я надеюсь, что это поможет.