====== 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''와는 조금 다르다. ''ApplicationContext''는 ''BeanPostProcessor'' 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;
}
}
''InstantiationTracingBeanPostProcessor''는 단순히 정의된다. 비록 이름을 가지고 있지는 않지만 bean이기 때문에 다른 bean과 같이 종속성은 삽입될 수 있다.
==== BeanFactoryPostProcesors를 사용한 확장(Customizing configuration metadata with BeanFactoryPostProcessors) ====
''org.springframework.beans.factory.config.BeanFactoryPostProcessor''는 ''BeanPostProcessor''와 의미적으로 비슷하지만, 큰 차이점 중 하나는 ''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 ===
''PropertyPlaceholderConfigurer''는 ''BeanFactory'' 정의로부터 property 값을 분리하기 위해 사용한다. 분리된 값은 Java ''Properties'' 형식으로 작성된 다른 파일로 분리된다. 이 방식은 주 XML 설정 파일을 변경하지 않고, 환경 변수 등을 변경될 때 유용하다 (예를 들어 database URLs, 사용자명, 패스워드 등).
classpath:com/foo/jdbc.properties
위 설정의 실제 값은 아래와 같다.
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
만약 Spring 2.5부터 지원되는 ''context'' namespace를 사용하면 다음과 같이 설정할 수 있다.
''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를 사용하면 다음과 같이 설정할 수 있다.
==== 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'' 그 자체를 요구하는 경우도 있다. 이런 경우, ''BeanFactory''의 ''getBean'' 메소드를 호출할 때 bean id 앞에 '''&'''를 붙이면 된다.
===== 참고자료 =====
* [[https://docs.spring.io/spring-framework/docs/5.3.20/reference/html/core.html#beans-factory-extension|Spring Framework - Reference Document / 1.8 Container Extension Points]]