developer tip

Spring XML 컨텍스트에서 조건부 리소스 가져 오기를 수행하는 방법은 무엇입니까?

optionbox 2021. 1. 6. 08:04
반응형

Spring XML 컨텍스트에서 조건부 리소스 가져 오기를 수행하는 방법은 무엇입니까?


내가 달성하고 싶은 것은 "동적으로"(즉, 구성 파일에 정의 된 속성을 기반으로) 자식 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메서드 에서 발생)의 동작을 확장하도록 재정의 합니다 . 그러나이 확장을 어디에서 등록 할 수 있는지 잘 모르겠습니다.

이제 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;
         }
    }
}

그리고 마지막으로 /com/example/context-fragment.xml에 포함하려는 빈 정의를 넣습니다.

@Conditional에 대한 JavaDoc을 참조하십시오.


Spring 4 이전에는 표준 Spring 컴포넌트를 사용할 수있는 가장 가까운 방법은 다음과 같습니다.

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

여기서 ${xyzzy}시스템 속성에서 속성을 보간합니다. (로드 프로세스를 시작하기 전에 다른 위치의 속성을 시스템 속성 개체에 추가하는 컨텍스트 로더 클래스의 해키 사용자 지정 버전을 사용합니다.)

그러나 불필요한 것들을 많이 가져 오는 것에서 벗어나 필요한 빈을 인스턴스화하도록 다양한 트릭을 사용할 수도 있습니다. 이러한 트릭에는 다음이 포함됩니다.

  • 자리 표시 자 및 속성 대체
  • 새로운 Spring 표현 언어를 사용하여 다른 빈을 선택하고,
  • 대상 이름에 자리 표시자가있는 빈 별칭,
  • 게으른 빈 초기화 및
  • 똑똑한 콩 공장.

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

if you're using different profiles in tests, just add -DforkMode=never to make sure that the tests will run inside same VM, therefore the param spring.profiles.active wont be lost


For the record, Robert Maldon explains how to accomplish conditional definition of beans in this post: http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html. It is a bit long to copy it here (besides, I don't think I should copy-paste his article anyway).

The end result with this approach, adapted for your example, is:

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

It is certainly not so simple as Stephen C's solution, but it is much more poweful.


Another option is to have your app load a modules-config.xml file that is located in the /conf folder and edit it during the install/config phase to uncomment the modules you want loaded.

This is the solution I'm using with a web application that serves as a container for different integration modules. The web application is distributed with all the different integration modules. A modules-config.xml is placed in tomcat's /conf folder and the conf folder is added to the classpath (via catalina.properties/common.loader property). My web app webapp-config.xml has a <import resource="classpath:/modules-config.xml"/> to get it loaded.


Another one to consider for Spring 3.0:

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

where ${xyzzy} interpolates a property from the system properties.


You can override contextInitialized(javax.servlet.ServletContextEvent event) in your own ContextLoaderListener and set required System property before super.contextInitialized(event) called like this

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);
    }
}

And than replace ContextLoaderListener to MyContextLoaderListener in your web.xml

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

Now you can use in your spring.xml

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

I hope this will help.

ReferenceURL : https://stackoverflow.com/questions/3035630/how-to-achieve-conditional-resource-import-in-a-spring-xml-context

반응형