Container extension points

개요

설명

Spring Framework의 IoC 컴포넌트는 확장을 고려하여 설계되었다. 일반적으로 어플리케이션 개발자가 다양한 BeanFactory 또는 ApplicationContext 구현 클래스를 상속받을 필요는 없다. Spring IoC container는 특별한 통합 interface의 구현체를 삽입하여 확장할 수 있다.

BeanPostProcessors를 사용한 확장(Customizing beans using BeanPostProcessors)

BeanPostProcessors interface는 다수의 callback 메소드를 정의하고 있는데, 어플리케이션 개발자는 이들 메소드를 구현함으로써 자신만의 객체화 로직(instantiation logic), 종속성 해결 로직(dependency-resolution logic) 등을 제공할 수 있다.

org.springframework.beans.factory.config.BeanPostProcessor interface는 두개의 callback 메소드로 구성되어 있다. 특정 class가 Container에 post-processor로 등록되면, post-processor는 container에서 생성되는 각각의 bean 객체에 대해서, container 객체화 메소드 전에 callback을 받는다.

중요한 것은 BeanFactory는 post-processor를 다루는 방식에 있어서 ApplicationContext와는 조금 다르다. ApplicationContextBeanPostProcessor interface를 구현한 bean을 자동적으로 인식하고 post-processor로 등록한다. 하지만 BeanFactory 구현을 사용하면 post-processor는 다음과 같이 명시적으로 등록되어야 한다.

ConfigurableBeanFactory factory = new XmlBeanFactory(...);
 
// now register any needed BeanPostProcessor instances
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(postProcessor);
 
// now start using the factory

명시적인 등록은 불편하기 때문에 대부분의 Spring 기반 어플리케이션에서는 순수 BeanFactory 구현보다는 ApplicationContext 구현을 사용한다.

Example: Hello World, BeanPostProcessor-style

본 예제는 올바른 예는 아니지만, 기본적인 사용 방법을 보여주기 위함이다.

package scripting;
 
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
 
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
 
    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean; // we could potentially return any object reference here...
    }
 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
 
    <lang:groovy id="messenger"
          script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/> 
    </lang:groovy>
 
    <!-- 
        when the above bean ('messenger') is instantiated, this custom
        BeanPostProcessor implementation will output the fact to the system console
     -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>
 
</beans>

InstantiationTracingBeanPostProcessor는 단순히 정의된다. 비록 이름을 가지고 있지는 않지만 bean이기 때문에 다른 bean과 같이 종속성은 삽입될 수 있다.

BeanFactoryPostProcesors를 사용한 확장(Customizing configuration metadata with BeanFactoryPostProcessors)

org.springframework.beans.factory.config.BeanFactoryPostProcessorBeanPostProcessor와 의미적으로 비슷하지만, 큰 차이점 중 하나는 BeanFactoryPostProcessors는 bean 설정 메타정보를 처리한다는 것이다. Spring IoC container는 BeanFactoryPostProcessors가 설정 메타정보를 읽고, container가 실제로 bean를 객체화하기 전에 그 정보를 변경할 수 있도록 허용한다.

Bean factory post-processor는 BeanFactory의 경우 수동으로 실행되고, ApplicationContext의 경우 자동으로 실행된다.

BeanFactory에서는 다음과 같이 수동으로 실행한다.

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
 
// bring in some property values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
 
// now actually do the replacement
cfg.postProcessBeanFactory(factory);

Example: the PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurerBeanFactory 정의로부터 property 값을 분리하기 위해 사용한다. 분리된 값은 Java Properties 형식으로 작성된 다른 파일로 분리된다. 이 방식은 주 XML 설정 파일을 변경하지 않고, 환경 변수 등을 변경될 때 유용하다 (예를 들어 database URLs, 사용자명, 패스워드 등).

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:com/foo/jdbc.properties</value>
    </property>
</bean>
 
<bean id="dataSource" destroy-method="close"
      class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

위 설정의 실제 값은 아래와 같다.

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

만약 Spring 2.5부터 지원되는 context namespace를 사용하면 다음과 같이 설정할 수 있다.

<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>

PropertyPlaceholderConfigurer는 사용자가 지정한 Properties 파일뿐 아니라 만약 지정한 property가 없을 경우, Java System properties도 검사할 수 있다. 이 기능은 systemPropertiesMode 설정을 통해 조절할 수 있다.

Example: the PropertyOverrideConfigurer

PropertyOverrideConfigurer는 또다른 bean factory post-processor로 PropertyPlaceholderConfigurer와 비슷하다. 하지만 PropertyPlaceholderConfigurer와는 반대로 원본 설정은 bean properties로 기본(default) 값을 가지거나 전혀 값을 가지지 않을 수 있다. 만약 Properties 파일이 특정 bean property를 위한 값을 가지고 있지 않을 경우, 기본 context definition이 사용된다. Properties 파일 설정의 각 줄은 다음과 같은 형식이다.

beanName.property=value

Spring 2.5부터 지원되는 context namespace를 사용하면 다음과 같이 설정할 수 있다.

<context:property-override location="classpath:override.properties"/>

FactoryBeans를 사용한 확장(Customizing instantiation logic using FactoryBeans)

org.springframework.beans.factory.FactoryBean interface를 구현한 객체는 스스로 fatory가 된다. FactoryBean interface는 Spring IoC container에 객체화 로직을 삽입할 수 있는 방법이다. 만약 복잡한 객체화 코드를 가지고 있어 장황한 XML 설정보다는 Java로 직접 표현하는 것이 더 좋은 경우, 객체화 코드를 가지고 있는 FactoryBean를 생성하여 container에서 삽입할 수 있다.

FactoryBean interface는 다음 3가지 메소드를 제공한다.

  • Object getObject(): 생성한 객체를 return한다. 생성된 객체는 공유될 수도 있다.
  • boolean isSingleton(): 만약 FactoryBean이 singleton을 return한다면 true'이다. 그렇지 않다면 false이다. * Class getObjectType(): getObject() 메소드에 의해 return되는 객체의 타입을 return한다. 만약 미리 알 수 없는 경우에는 null을 return한다. Container에게 FactoryBean이 생성한 객체가 아닌 FactoryBean 그 자체를 요구하는 경우도 있다. 이런 경우, BeanFactorygetBean 메소드를 호출할 때 bean id 앞에 '&'''를 붙이면 된다.

참고자료

 
egovframework/rte/fdl/ioc_container/container_extension_points.txt · 마지막 수정: 2023/12/21 05:21 (외부 편집기)
 
이 위키의 내용은 다음의 라이센스에 따릅니다 :CC Attribution-Noncommercial-Share Alike 3.0 Unported
전자정부 표준프레임워크 라이센스(바로가기)

전자정부 표준프레임워크 활용의 안정성 보장을 위해 위험성을 지속적으로 모니터링하고 있으나, 오픈소스의 특성상 문제가 발생할 수 있습니다.
전자정부 표준프레임워크는 Apache 2.0 라이선스를 따르고 있는 오픈소스 프로그램입니다. Apache 2.0 라이선스에 따라 표준프레임워크를 활용하여 발생된 업무중단, 컴퓨터 고장 또는 오동작으로 인한 손해 등에 대해서 책임이 없습니다.
Recent changes RSS feed CC Attribution-Noncommercial-Share Alike 3.0 Unported Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki