3.Bean Definition Profiles

실제 상황에서 필요한 가장 빈번한 요구사항은 흔히 개발과 운영으로 구분되는 각각의 환경 하에서 서로 다른 bean 설정을 등록하도록 하는 기능이다. 아마 가장 흔한 사례는 개발 단계에서 일반적인 datasource 를 사용하다가 운영 단계로 배포시 JNDI 기반의 datasource 를 사용하도록 하는 것이다. Spring 3.1 버전부터 제공되는 Bean Definition Profiles 기능은 이런 경우를 해결하기 위한 일반적인 방법을 제시한다.

3.1.XML Profiles

다음은 movieService, movieDao와 함께 standalone 형태의 datasource를 설정한 context-movie.xml 예제이다.

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
    xsi:schemaLocation="..."> 

    <bean id="movieService"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieServiceImpl"> 
        <property name="movieDao" ref="movieDao"/> 
    </bean> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>
	
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> 
        <property name="driverClass" value="org.hsqldb.jdbcDriver" /> 
        <property name="url" value="jdbc:hsqldb:file:/./db/sampledb" /> 
    </property name="username" value="sa" /> 
</beans>

상기 예제에서는 Spring 3.0 버전부터 제공되는 jdbc 네임스페이스를 활용하여 내장 데이터베이스 유형을 간단히 설정하고 있다. 만약 운영단계에서 JNDI 기반의 datasource를 사용해야 한다면 위의 설정은 다음과 같이 변경되어야 한다.

<beans ...> 
    <bean id="movieService" ... /> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> 
</beans>

각 설정들은 개별적으로 잘 동작하겠지만, 개발과 운영이 전환되어야 하는 상황에서는 위의 두가지 설정 내용을 어떻게 매끄럽게 전환할 것인지 고려해야 할 것이다. 다양한 해결책이 있겠지만 일반적으로 시스템의 환경 변수를 활용하거나, ${placeholder] 토큰을 활용한 <import/> XML 태그를 사용하여 해결한다.

3.1.1.XML 기반의 Profile 적용

Spring 3.1 버전부터는 <beans/> 태그가 profile 개념을 포함하게 되었다. 예제에서의 설정 파일은 이제 다음과 같이 3개의 설정으로 나뉘어 질 수 있는데, context-datasource-*.xml 파일의 profile 속성을 유의해서 살펴보기 바란다. 아래 설정 파일은 context-movie.xml 설정 파일의 내용이다.

<beans ...> 
    <bean id="movieService" ... /> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>
</beans>

다음은 context-datasource-dev.xml 설정 파일의 내용이다. beans 태그의 profile 속성으로 "dev" 가 정의되어 있다.

<beans profile="dev"> 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:file:/./db/sampledb" />
        <property name="username" value="sa" />
    </bean>
</beans>

다음은 context-datasource-production.xml 설정 파일의 내용이다. beans 태그의 profile 속성으로 "production"이 정의되어 있다.

<beans profile="production"> 
    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

3.1.2.중첩된 beans 태그

Spring 3.1 버전부터는 <beans/> 태그를 동일한 파일 내에 중첩하여 사용할 수 있다. 앞 절에서 설명된 3개의 xml 파일을 하나의 설정 파일로 정의하면 다음과 같다.

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
    xsi:schemaLocation="..."> 

    <bean id="movieService"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieServiceImpl"> 
        <property name="movieDao" ref="movieDao"/> 
    </bean> 

    <bean id="movieDao"
        class="org.anyframe.plugin.core.moviefinder.service.impl.MovieDao">
        <property name="dataSource" ref="dataSource"/>        
    </bean>
			
    <beans profile="dev"> 
		<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    		<property name="driverClass" value="org.hsqldb.jdbcDriver" />
    		<property name="url" value="jdbc:hsqldb:file:/./db/sampledb" />
    		<property name="username" value="sa" />
		</bean>
    </beans>

    <beans profile="production"> 
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

이러한 중첩 <beans/> 태그는 Beans Definitioin Profiles 용이 아닌 일반적인 용도로도 유용하다. 예를 들어 <beans/> 태그의 default-* 속성들 (default-lazy-init, default-init-method, default-destory-method 등)을 여러개의 bean 에 한번에 정의하고자 할 때에도 활용할 수 있다.

3.2.Profile 활성화

3.2.1.Programmatic Profile 활성화

이제 코드에서 명시적으로 ApplicationContext를 통하여, 앞서 정의된 profile 을 활성화 시키는 방법을 살펴보기로 하자. 다음 코드는 "dev" profile 을 활성화 시키는 예제이다.

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); 
ctx.getEnvironment().setActiveProfiles("dev"); 
ctx.load("classpath:/spring/context-*.xml"); 
ctx.refresh();

위의 코드를 실행시키는 경우 각 설정 파일들은 컨테이너에서 다음과 같이 처리된다.

  • context-movie.xml 은 profile 속성이 정의되지 않았으므로 항상 파싱되어 처리된다.

  • context-datasource-dev.xml 은 profile="dev"로 설정되어 있으며, 현재 "dev" profile 이 활성되어 있으므로 파싱되어 처리된다.

  • context-datasource-production.xml 은 profile="production"로 설정되어 있으며, 현재 "production" profile 이 활성되어 있지 않으므로 처리가 생략된다.

3.2.2.선언적인 Profile 활성화

기 정의된 profile은 programmatic한 방식 이외에도 시스템 환경 변수, JVM 시스템 프로퍼티, web.xml내 서블릿 컨텍스트 파라메터 혹은 JNDI 엔트리 등으로 설정된 spring.profiles.active 속성을 통하여 선언적인 방법을 통하여 활성화시킬 수 있다.

<servlet> 
    <servlet-name>dispatcher</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <init-param> 
        <param-name>spring.profiles.active</param-name> 
        <param-value>production</param-value> 
    </init-param> 
</servlet>

3.2.3.다중 Profile 활성화

하나의 Profile 만 활성화시킬 수 있는 것이 아니라, 여러개의 Profile 을 활성화 시킬 수도 있다. 다음은 programmatic 한 예제이다.

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

선언적인 방법으로는, spring.profiles,active 값은 콤마로 구분된 profile 명을 리스트 형태로 가질 수 있다.

-Dspring.profiles.active="profile1,profile2"

다음과 같이 Bean 정의시 하나 이상의 profile 을 표기할 수도 있다.

<beans profile="profile1,profile2"> 
    ... 
</beans>

3.3.@Profile

Bean Definition Profiles 정의시 Annotation 을 활용하여 정의할 수도 있는데, 이때 사용되는 것이 @Profile 태그이다. 이해를 위하여 다음장에 이어서 나오는 Java Based Configuration 의 내용을 함께 참고하는 것이 좋다.

3.3.1.Annotation 기반의 Profile 적용

XML 기반의 Profile 적용에서 언급되었던 XML 설정 파일의 내용을 Annotation 기반의 Java Based Configuration 으로 변환하면 다음과 같다.

@Configuration
@Profile("dev")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new SimpleDriverDataSource(
            new org.hsqldb.jdbcDriver(),
            "jdbc:hsqldb:file:/./db/sampledb",
            "sa",
            "");
    }

}
@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }

}

동일한 방법으로 XML 기반의 MovieService 설정은 다음과 같이 Java Based Configuratoin 으로 변경될 수 있다.

@Configuration
public class MovieServiceConfig {

    @Autowired
    DataSource dataSource;

    @Bean
    public MovieService movieService() {
        return new MovieService(movieDao());
    }

    @Bean
    public MovieDao movieDao() {
        return new MovieDao(dataSource);
    }
}

이 때 @AutoWired 를 통해 인젝션되는 dataSource 는 다음과 같이 활성화된 profile 에 따라 결정된다.

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.scan("org.anyframe.plugin.core.moviefinder");
ctx.refresh();

3.4.Environment Abstraction (Environment 추상화)

3.4.1.PropertySources

Spring 3.1부터 제공되는 Environment 추상화 기능은 우선순위 설정이 가능한 PropertySource 에 대하여 통합 검색 옵션을 제공한다.

ApplicationContext ctx = new GenericApplicationContext(); 
Environment env = ctx.getEnvironment(); 
boolean containsFoo = env.containsProperty("foo"); 
System.out.println("Does my environment contain the 'foo' property? " + containsFoo);

위의 코드를 살펴보면 'foo' 라는 속성이 현재 환경 내에서 정의되어 있는지를 확인하고 있다. 해당 속성값을 검색하기 위해 Environment 객체는 PropertySource 객체들에 대하여 검색을 수행한다.

ProyertySource 란 키-값 쌍으로 구성된 속성들의 추상화를 나타내는데, Spring 의 DefaultEnvironment 는 다음과 같이 두개의 PropertySource 로 구성되어 있다.

  • JVM 시스템 속성값의 집합 (System.getProierties()와 같은 방식)

  • 시스템 환경 변수의 집합 (System.getenv()와 같은 방식)

즉, 'foo' 라는 이름으로 시스템 속성값이 정의되어 있거나, 환경 변수가 정의되어 있다면 env.containsProperty("foo")의 값은 true 가 될 것이다. 검색은 순차적으로 수행되는데, 기본적으로 시스템 속성이 환경 변수보다 우선 순위가 높다. 따라서 env.getProperty("foo")을 실행하게 되면 시스템 속성값이 우선적으로 반환된다. 중요한 점은, 이러한 동작 원리에 대하여 설정 변경이 가능하다는 것이다. 만약 사용자 정의 PropertySource 를 만들었다면 순차적인 검색에 포함되도록 설정할 수 있다.

ConfigurableApplicationContext ctx = new GenericApplicationContext(); 
MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); 
sources.addFirst(new MyPropertySource());

위의 예제 코드에서는 MyPropertySource 를 가장 높은 우선순위로 검색되도록 추가하는 모습을 나타내고 있다. 만약 원하는 값을 MyPropertySource 에서 찾지 못한다면 다른 PropertySource 에서 우선 순서에 따라 검색하게 된다.

3.4.2.PropertySource 활용

이제 실제 개발시 어플리케이션 상에서 어떻게 Environment 와 PropertySource 를 사용할 수 있는지 살펴보도록 한다.

  1. <import/> 구문 내에서의 ${placeholder} 처리

    <beans> 
        <import resource="spring/${customer}-config.xml"/> 
    </beans>

    고객 정보에 따라 각기 다른 설정 파일을 로드해야 하는 경우 <import/> 구문과 placeholder 를 통해 'customer' 값을 결정하게 된다. Spring 3.1 이전에는 placeholder 의 값은 오로지 JVM 시스템 속성이나 시스템 환경 변수에서만 검색되었다. 하지만 Spring 3.1 이후부터 Environment 추상화를 통해 검색 우선 순위를 조정할 수 있을 뿐만 아니라 사용자 정의 PropertySource 를 적절히 활용할 수 있게 되었다.

  2. bean 정의내에서의 ${placeholder} 처리

    <context:property-placeholder location="com/bank/config/datasource.properties"/> 
    
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
        <property name="driverClass" value="${database.driver}"/> 
        <property name="jdbcUrl" value="${database.url}"/> 
        <property name="username" value="${database.username}"/> 
        <property name="password" value="${database.password}"/> 
    </bean>

    일반적으로 Spring에서 bean 정의시 사용된 ${...} placeholder 를 대치하기 위해서 PropertyPlaceholderConfigurer 나 <context:property-placeholder/>를 사용한다. Spring 3.1 버전부터는 <context:property-placeholder/> 에서 더이상 PropertyPlaceholderConfigurer 를 등록하지 않고 PropertySourcesPlaceholderConfigurer 를 등록하는데, 이 컴포넌트는 기존과 동일하게 datasource.properties 파일 내에서 ${database.*} 속성값을 찾지만 속성값을 찾는데 실패한 경우에는 대안으로써 현재의 Environment 객체의 PropertySource 를 사용한다. 이러한 개선 이전에는 속성값 검색 실패시, 대안으로 오직 시스템 속성값과 환경 변수만을 사용할 수 있었지만, 이제 개발자 측면에서 더 다양한 PropertySource 를 추가적으로 활용할 수 있게 되었다.

3.4.3.웹 어플리케이션내의 PropertySource 활용

상당수의 Spring 기반 어플리케이션은 웹 어플리케이션이며, ApplicationContext가 ContextLoaderListener에 의해 관리된다. 이제 PropertySource 를 제어하기 위하여 Spring 에서 제공하는 ApplicationContextInitializer 인터페이스를 구현하고, 서블릿 컨텍스트 파라메터에 contextInitializerClasses 값을 정의한 예제를 살펴보기로 한다. web.xml 의 일부는 다음과 같다.

<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>com.bank.MyInitializer</param-value>
</context-param>

ApplicationContextInitializer를 구현한 에제 클래스는 다음과 같다.

public class MyInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
    public void initialize(ConfigurableWebApplicationContext ctx) {
        PropertySource ps = new MyPropertySource();
        ctx.getEnvironment().getPropertySources().addFirst(ps);
        // 기타 컨텍스트 초기화 작업 수행 ....
    }
}

ApplicationContextInitializer를 구현하고 이를 등록함으로써, 간단한 방법으로 웹 어플리케이션의 ApplicationContext가 refresh 되기 전에 추가 처리를 할 수 있다.


출처 - http://dev.anyframejava.org/docs/anyframe/plugin/essential/core/1.5.0/reference/html/ch03.html






일반적인 PropertyPlaceholderConfigurer
org.springframework.beans.factory.config 패키지 안에 속해 있다.

java.lang.Object
    - org.springframework.core.io.support.PropertiesLoaderSupport
        - org.springframework.beans.factory.config.PropertyResourceConfigure
            - org.springframework.beans.factory.config.PropertyPlaceholderConfigurer

public class PropertyPlaceholderConfigurer
extends PropertyResourceConfigurer
implements BeanNameAware, BeanFacotyAware

외부에 저장된 프로퍼티 파일로 부터 bean definition으로 값을 끌어 당길 수 있다. (외부 프로퍼티 값을 빈 정의부로 가져올 수 있다는 뜻)
Ant / Log4J / JSP EL(Expression Language) 스타일 처럼 ${...} 표기법을 사용한다.

주로 DB 설정값만을 저장해 놓은 Property 파일로 부터 값을 읽어와서 bean definition에서 ${...} 표기법을 통해 사용할 수 있다.

<bean id="dataSource" class="org.springframework.jdbc.dataSource.DriverManagerDataSource">
    <property name="drivateClassName"><value>${dirver}</value></property>
    <property name="url"><value>jdbc:${dbname}</value></property>
</bean>

위의 bean definition 에서 보면 위에 설명한 내용이 그대로 반영되어 있다.

이렇게 bean definition에서 사용하기 위해서는 선행적으로 PropertyPlaceholderConfigurer을 선언해야 한다.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
         <value>classpath:config/jdbc.properties</value>
    </property>
</bean>

PropertyPlaceholderConfigurer에서는 location, 즉 setLocation 메소드가 존재하지 않고 상속 구조의 상단에 해당하는 PropertiesLoaderSupport 클래스까지 가야지 찾을 수 있다.

PropertiesLoaderSupport 클래스의 setLoaction 메소드의 인자로는 Resource 타입을 전달해 주면 된다. 클래식한 properties 파일이나 JDK 1.5의 properties XML 포맷이면 가능하다.
또한, 여러개의 properties 파일을 설정하고자 하면 setLocations(bean definition에서는 locations) 메소드가 있고 <list> 태그를 사용하여 properties를 설정하면 된다.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:cars.properties</value>
          <value>classpath:airplanes.properties</value>
        </list>
    </property>
</bean>

setLocation(bean definition에서는 location) 메소드와 동일하게 클래식 properties 파일이나 JDK 1.5 properties XML 형식으로 읽어들일 properties 파일을 구성하면 된다.


Spring 2.5에서의 PropertyPlaceholderConfigurer
Spring 2.5에서는 위의 작업들이 보다 간편하게 발전하였는데, 알아보자.
위의 몇 개 과정을 통합해서 하나의 <context:property-placeholder> 태그로 지원하고 있다. 

<context:property-placeholder> 태그를 사용하기 위해서는 몇 가지 확인해야 할 포인트들이 있다.
1. XML Schema 기반의 Bean definition 
2. XML namespace 선언
3. 2.5 기반의 태그 사용

XML Schema 기반이 아니라, 굳이 DTD 기반의 Bean definition이라면 맨 위에서 쓴 대로 기존 방식을 사용하면 된다. 

<beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
            xsi:schemaLocation="http://www.springframework.org/schema/beans
                                    http://www.springframework.org/schema/spring-beans-2.5.xsd
                                    http://www.springframework.org/schema/context
                             http://www.springframework.org/schema/spring-context-2.5.xsd
">

<context:property-placeholder location="classpath:config/jdbc.properties" />
(이 다음 부터는 DTD 기반과 사용법은 동일합니다.)


만약 여러개의 properties를 추가하고 싶다면, 콤마(,)를 구분자로 여러개의 properties를 적어주면 된다.
(multiple locations may be provied as a comma-seperated list - from Spring 2.5 reference)

하지만, 이 부분은 확인이 필요한 부분인데 Spring forum에 올려진 질문을 보면 콤마(,)로 구분자를 사용했음에도 불구하고 invalid 에러를 일으킨다라는 글이 있다.

또한 properties 안의 내용이 아니라 글자 그대로 출력되는 경우가 있어, 좀더 연구해 봐야 할 주제다.

 

 


출처 - http://blog.naver.com/PostView.nhn?blogId=tyboss&logNo=70048117732









 

Spring framwork의 IoC 컨테이너 XML 설정 파일을 사용하면 애플리케이션을 유연하게 구성할 수 있습니다. 배포 후에도 지역 상황에 맞춰서 컴포넌트를 다른 것으로 바꾼다거나 운영 설정 값을 바꾸거나 하는 일이 무척 쉽지요.  하지만, 이 파일에는 가볍게 다루기에는 너무 중요한 정보들이 들어 있습니다. 사실 객체 생성 로직이 들어 있는 코드라고 볼 수도 있습니다.

중요한 정보를 가진 파일에 환경에 따라서 자주 변경되는 런타임 설정값들은 같이 두는 것은 적당하지 않습니다. 자주 변경할 것 같은 값들을 컨테이너 설정 파일 외부로 빼서 별도 파일로 관리하는 것이 좋겠지요. spring에 이 용도로 제공하는 후처리기가 두 가지 있는데 하나는 PropertyPlaceholderConfigurer이고 또 하나는 PropertyOverrideConfigurer입니다.

PropertyPlaceholderConfigurer는 컨테이너 설정 파일의 빈들을 만들기 전에 설정 파일 중에 있는 임시표기(Placeholder를 우리말로 뭐라고 해야 할지...ㅡ.ㅡ)를 Properties 파일에 등록한 설정값으로 바꿔줍니다. PropertyOverrideConfigurer는 반대로 빈들을 다 만들고 DI까지 다 완료한 이후에 Properties 파일에 지정한 빈 이름과 필드명에 지정된 값들로 덮어씁니다.

전자는 property 이름을 빈의 구현과 관계없이 논리적으로 정할 수 있는 대신 컨테이너 설정 파일의 설정값이 들어가야 할 위치에 property 이름으로 임시표식을 삽입해줘야 합니다. 후자는 컨테이너 설정 파일을 전혀 손대지 않고도 기본으로 지정된 값을 배치된 운영 환경에 맞는 값으로 덮어쓸 수 있습니다. 하지만 property 이름이 빈 이름에 종속됩니다.

제약

이 두 가지 후처리기를 사용하면 운영 환경에 따라 바뀌는 값을 외부로 뺄 수 있지만 여전히 제약이 남아있습니다. 둘 다 외부 설정 파일로 Properties 파일만 사용할 수 있다는 것이지요. Properties 파일로도 큰 문제 없이 설정을 할 수 있지만 이런 요구가 있을 수 있습니다.

  • Poperties 파일 대신 XML 파일을 쓰고 싶다.
  • DB에 들어 있는 설정 값들로 객체를 만들고 싶다.
  • DB와 XML, Properties 파일을 다 쓰고 싶다.
  • JNDI Tree에 설정값을 넣고 싶다.
  • Windows의 ini 파일이나 Mac의 PropertyList 파일을 쓰고 싶다.

저는 인코딩 문제도 있고 읽기도 좋아서 Properties 보다는 XML을 선호합니다. 그리고 몇몇 설정값은 사용자에게 인터페이스를 제공해서 DB의 값을 조작하게 해야 하는 경우가 있습니다.


Apache Commons Configuration

애플리케이션 운영 환경 설정에 제가 주로 사용하는 기술이 Apache commons 프로젝트 중 한 컴포넌트인 Configuration 입니다.  홈페이지에 의하면 Commons Configuration은 일반화된 설정 인터페이스를 제공함으로써 자바 애플리케이션이 다양한 소스에서 설정을 읽을 수 있도록 해준다고 설명하고 있습니다.

열거하고 있는 소스는 다음과 같습니다.

  • Properties 파일
  • XML 문서
  • Windows INI 파일
  • Property list 파일 (plist)
  • JNDI
  • JDBC Datasource
  • System properties
  • Applet parameters
  • Servlet parameters

Commons Configuration은 이렇게 여러 소스에서 설정값을 읽어오는 기능 외에도 복잡한 XML을 효율적으로 다룰 수 있고  Properties 파일도 기본 API보다 훨씬 다양한 기능을 제공하고 있으며 다양한 타입 접근자를 가지고 있고 여러 소스의 설정값을 통합할 수 있습니다. 자세한 것은 사용자 설명서가 잘 되어 있으니 참고하십시오.

CommonConfigurationFactoryBean

Spring modules 프로젝트를 보면 Apache Commons의 여러 컴포넌트들 중 Configuration, Lang, Chain, Validator의 스프링 통합 컴포넌트들이 있습니다. 그중 Configuration 스프링 통합 컴포넌트가 CommonConfigurationFactoryBean 입니다. 이 빈은 아답터의 일종인데 PropertyPlaceholderConfigurer 같이 PropertyResourceConfigurer를 상속한 빈들에 Commons Configuration의 각종 configuration 객체를 붙일 수 있도록 해줍니다.

CommonConfigurationFactoryBean에 하나 이상의 소스에서 설정값을 읽은 Configuration을 등록한 후 PropertyPlaceholderConfigurer나 PropertyOverrideConfigurer의 properties 필드에 대입을 해주면 CommonConfigurationFactoryBean는 등록된 configuration들에 담겨있는 설정값들을 Properties로 변환해서 전달해줍니다. 

사실 CommonConfigurationFactoryBean은 내부적으로 Configuration의 CompositeConfiguration를 가지고 있어서 여러 소스의 Configuration을 합 할 수 있습니다. 

myconfig.xml이라는 XML 설정 파일을 사용해서 PropertyPlaceholderConfigurer를 등록하는 스프링 설정은 이렇습니다.

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
                <property name="configurations">
                    <list>
                        <bean class="org.apache.commons.configuration.XMLConfiguration">
                            <constructor-arg type="java.lang.String">
                                <value>myconfig.xml</value>
                            </constructor-arg>
                        </bean>
                    </list>
                </property>
            </bean>
        </property>
    </bean>    

단순한 설정인데도 좀 복잡하게 나왔습니다.  보시면 PropertyPlaceholderConfigurer와 CommonsConfigurationFactoryBean와 XMLConfiguration 세 가지 객체가 만들어집니다.

PropertyPlaceholderConfigurer는 위에 설명한 것처럼 스프링 설정에 환경 설정값을 넣어주는 후처리기입니다. XMLConfiguration은 생성자에 지정한 XML 파일을 읽어서 설정값을 담은 Commons Configuration의 configuration 객체 중 하나입니다. CommonsConfigurationFactoryBean는 XMLConfiguration을 받아서 설정값을 Properties로 변환한 후 PropertyPlaceholderConfigurer에게 전달하는 역할을 합니다.

기본값과 로컬 설정의 분리

별도 운영 설정 파일을 만들더라도 모든 설정을 해줄 필요 없이 변경이 필요한 한두 설정값만  변경하고 나머지는 기본값이 대응되도록 하는 것이 운영하는데 도움이 됩니다. 다음과 같이 local_config.xml과 default_config.xml을 만들어서 각각 로컬 설정과 기본 설정 값을 넣도록 합니다.

local_config.xml

<config>
<smtp>
  <host>mail.abc.co.kr</smtp_host>
</smtp>
</config>

default_config.xml

<config>
<smtp>
  <host>mail.myhost.co.kr</smtp_host>
  <port>25</port>
</smtp>
</config>

그리고 이 두 파일을 사동하도록 설정합니다.

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
                <property name="configurations">
                    <list>
                        <bean class="org.apache.commons.configuration.XMLConfiguration">
                            <constructor-arg type="java.lang.String">
                                <value>local_config.xml</value>
                            </constructor-arg>
                        </bean>
                        <bean class="org.apache.commons.configuration.XMLConfiguration">
                            <constructor-arg type="java.lang.String">
                                <value>default_config.xml</value>
                            </constructor-arg>
                        </bean>
                    </list>
                </property>
            </bean>
        </property>
    </bean> 

   <bean id="mailService" class=".....">
        <property name="host" value="${smtp.host}"/>
        <property name="port" value="${smtp.port}"/>
   </bean>

이렇게 local_config.xml을 먼저 등록하고 default_config.xml을 나중에 등록하면 먼저 local_config.xml의 설정값을 먼저 찾고 만약 이 파일에 찾으려는 설정값이 없으면 그다음 configration인 default_config.xml에서 찾는다.

Configuration을 직접 사용

Commons Configuration은 Properties로 변환해서 컨테이너 설정 파일의 설정값을 지정하는 용도 외에도 복잡한 애플리케이션 설정값을 표현하는 능력이 있습니다. 이런 Configuration의 기능을 직접 쓰고 싶은 경우를 위해서 설정을 분리하고 필요한 객체에서 참조할 수 있도록 하면 유용합니다.

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
               <property name="configurations">
                    <list>
                         <ref bean="configuration"/>
                    </list>
                </property>
            </bean>
        </property>
    </bean>
    
    <bean id="configuration" class="org.apache.commons.configuration.CompositeConfiguration">
        <constructor-arg>
            <list>
                <bean class="org.apache.commons.configuration.XMLConfiguration">
                    <constructor-arg type="java.lang.String">
                        <value>user-config.xml</value>
                    </constructor-arg>
                </bean>
                <bean class="org.apache.commons.configuration.XMLConfiguration">
                    <constructor-arg type="java.lang.String">
                        <value>default-config.xml</value>
                    </constructor-arg>
                </bean>
            </list>
        </constructor-arg>
    </bean>

   <bean id="mailService" class=".....">
        <property name="host" value="${smtp.host}"/>
        <property name="port" value="${smtp.port}"/>
   </bean>

   <bean id="anotherService" class=".....">
        <property name="configuration" ref="configuration"/>
   </bean>

anotherService는 직접 Configuration 객체를 받아서 그 안에 담겨있는 설정값들을 Commons Configuration이 제공하는 다양한 방법으로 읽어서 작동할 때에 참고할 수 있습니다.

 


출처 - http://gyumee.egloos.com/1471952





 


 

<bean id="ttt" class="com.foo.bar">
  <property name="properties">
    <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
      <property name="location">
        <value>classpath:test.properties</value>
      </property>
    </bean>
  </property>
</bean>



<bean id="sqlProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> 
    <property name="location" value="/WEB-INF/sql.properties"/>
</bean>                                               
                                   
즉, 위에서 "sqlProps"는 java.util.Properties 타입의 컴포넌트 빈이 되는 것입니다. 그리고 그 소스 파일은, location 프로퍼티에 지정된 값이죠.
                          
                                                            
그 런데, 프로퍼티 파일을 하나로부터가 아니라, 여러개로부터 읽어들여서 하나의 java.util.Properties 객체를 만들고 싶다면, 다음과 같이 하시면 됩니다. 즉, location 대신에, locations 프로퍼티를 사용하고, 이것의 값을 List로 설정하는 것이죠. 다음 예는, sql1.properties 파일과 sql2.properties 파일을 모두 읽어 들여, sqlProps 라는 객체에 설정하는 것입니다.
             
<bean id="sqlProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">                                                 
    <property name="locations">                                                 
        <list>                                                             
            <value>/WEB-INF/sql1.properties</value>                                         
            <value>/WEB-INF/sql2.properties</value>                                         
        </list>                                                             
    </property>                                                             
</bean>                                                                 
                                                           
참고로, 컴포넌트 빈의 프로퍼티의 타입이 배열인 경우 (예를 들어, String [], Resource [] 등과 같이), Spring Framework에서는 기본적으로 <list> 형태로 프로퍼티를 설정하시면 됩니다.
                                                           
그리고, 프로퍼티 파일의 value들이 한글로 들어가 있는 경우, 인코딩을 다음과 같이 설정하셔야만 합니다.                     

<bean id="sqlProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> 
    <property name="fileEncoding" value="EUC-KR" />                                         
    <property name="location" value="/WEB-INF/sql.properties"/>
</bean>




'Framework & Platform > Spring' 카테고리의 다른 글

spring data - mongodb 설정 예제  (0) 2013.05.19
spring - ehcache config  (0) 2013.04.16
spring oxm  (0) 2013.01.10
spring java based configuration  (0) 2013.01.06
spring data jpa - Unable to build EntityManagerFactory  (0) 2013.01.05
Posted by linuxism
,