Подтвердить что ты не робот

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

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

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

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

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

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

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

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

  • Переопределение DefaultBeanDefinitionDocumentReader для расширения поведения синтаксического анализа и интерпретации элемента import (что происходит там в методе importBeanDefinitionResource). Однако я не уверен, где я могу зарегистрировать это расширение.
4b9b3361

Ответ 1

Теперь это возможно, используя Spring 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

Ответ 2

До Spring 4 наиболее близкими к использованию стандартных компонентов Spring являются:

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

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

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

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

Ответ 3

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

Ответ 4

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

<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it 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, чтобы убедиться, что тесты будут выполняться внутри одной виртуальной машины, поэтому параметр spring.profiles.active не будет потерян

Ответ 5

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

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

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

Это, конечно, не так просто, как решение Stephen C, но оно намного более сильное.

Ответ 6

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

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

Ответ 7

Еще один вопрос для Spring 3.0:

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

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

Ответ 8

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

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 в вашем файле web.xml.

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

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

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

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