Как добиться условного импорта ресурсов в 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
). Однако я не уверен, где я могу зарегистрировать это расширение.
Ответы
Ответ 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}" />
Я надеюсь, это поможет.