이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.
4.6 빈의 특성 커스터마이징하기
4.6.1 라이프사이클 콜백
컨테이너의 빈 라이프사이클 관리와 상호작용하기 위해 스프링의 InitializingBean과 DisposableBean을 구현할 수 있다. 빈의 생성과 소멸에서 어떤 행동을 하도록 컨테이너는 InitializingBean에 대해서 afterPropertiesSet()를 호출하고 DisposableBean에 대해서 destroy()를 호출한다. 초기화 메서드와 소멸 메서드의 객체 정의 메타데이터를 사용하면 클래스가 스프링 인터페이스와 커플링을 갖지 않으면서 이와 동일한 컨테이어와의 통합을 이룰 수 있다.
내부적으로 스프링 프레임워크는 콜백 인터페이스가 적절한 메서드를 찾고 호출할 수 있도록 처리하기 위해 BeanPostProcessor 구현체를 사용한다. 어떤 특징들을 커스터마이징하거나 스프링이 제공하지 않는 라이프사이클 동작이 필요하다면 직접 BeanPostProcessor를 구현할 수 있다. 더 자세한 내용은 Section 4.8, “Container Extension Points”를 참고해라.
초기화 콜백과 소멸화 콜백에 추가적으로 컨테이너의 라이프사이클에 의해서 주도되듯이 스프링이 관리하는 객체들을 시작과 종료 과정에 포함할 수 있도록 Lifecycle 인터페이스를 구현할 수도 있다.
라이프사이클 콜백 인터페이스는 이 섹션에서 설명한다.
4.6.1.1 초기화 콜백
org.springframework.beans.factory.InitializingBean 인터페이스는 컨테이너가 빈의 모든 필수 프로퍼티를 설정한 후에 빈이 초기화를 하도록 한다. InitializingBean 인터페이스는 단일 메서드를 지정한다.
1 |
void afterPropertiesSet() throws Exception; |
코드가 불필요하게 스프링에 의존성을 가지게 되기 때문에 InitializingBean 인터페이스를 사용하는 것을 권하지 않는다. 대신에 POJO 초기화 메서드를 지정해라. XML 기반의 설정 메타데이터에서는 void에 아규먼트가 없는 시그니처를 가진 메서드의 이름을 지정하는데 init-method 속성을 사용해라. 예를 들어 다음과 같이 정의한다.
1 |
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> |
1 2 3 4 5 |
public class ExampleBean { public void init() { // 어떤 초기화 동작을 수행한다 } } |
이는 다음과 동일하다.
1 |
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/> |
1 2 3 4 5 |
public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() { // 어떤 초기화 동작을 수행한다 } } |
하지만 코드가 스프링에 의존성을 갖지 않는다.
4.6.1.2 소멸화 콜백
org.springframework.beans.factory.DisposableBean 인터페이스을 구현하면 빈을 담고 있는 컨테이너가 파괴될 때 빈이 콜백을 얻을 수 있도록 한다. DisposableBean 인터페이스는 단일 메서드를 지정한다.
1 |
void destroy() throws Exception; |
DisposableBean 콜백을 사용하면 코드가 스프링에 불필요한 의존성을 가지기 때문에 추천하지 않는다. 대신에 빈 정의에서 지원하는 제너릭 메서드를 지정해라. XML 기반의 설정 메타데이터를 사용하면 <bean/>의 destroy-method 속성을 사용해라. 예를 들면 다음과 같이 정의한다.
1 |
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> |
1 2 3 4 5 |
public class ExampleBean { public void cleanup() { // 어떤 소멸화 작업을 수행한다 (풀에 있는 커넥션을 릴리즈하는 것같은) } } |
이는 다음과 같다.
1 |
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/> |
1 2 3 4 5 |
public class AnotherExampleBean implements DisposableBean { public void destroy() { // 어떤 소멸화 작업을 수행한다 (풀에 있는 커넥션을 릴리즈하는 것같은) } } |
하지만 코드가 스프링에 의존하지 않는다.
4.6.1.3 기본 초기화 메서드와 파괴 메서드
스프링에 의존적인 InitializingBean와 DisposableBean 콜백 인터페이스를 사용하지 않은 초기화와 파괴과 메서드 콜백을 작성할 때 보통 init(), initialize(), dispose() 등과 같은 메서드 이름을 사용한다. 원칙적으로 모든 개발자가 같은 이름을 사용하고 일관정을 보장하기 위해 이러한 라이프사이클 콜백 메서드 이름은 프로젝트를 넘어 표준화되어 있다.
모든 빈에서 이름있는 초기화 콜백 메서드와 파괴 콜백 메서드를 기다리도록(look) 스프링 컨테이너를 설정할 수 있다. 이 말은 어플리케이션 개발자가 빈 정의마다 init-method="init" 속성을 설정할 필요없이 어플리케이션 클래스들을 작성하고 init()라는 초기화 콜백을 사용할 수 있다는 의미이다. 빈이 생성될 때 스프링 IoC 컨테이너는 이 메서드를 호출한다.(그리고 앞에서 설명한 표준 라이프사이클 콜백 계약에 따라서) 이 기능은 초기화와 파괴 메서드 콜백에 일관성있는 작명 관례를 강제하는 역할도 한다.
초기화 콜백 메서드가 init()이고 파괴 콜백 메서드가 destroy()라고 해보자. 클래스는 다음 예제의 클래스와 비슷할 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) { this.blogDao = blogDao; }
// (놀랍지는 않지만) 이는 초기화 콜백 함수다 public void init() { if (this.blogDao == null) { throw new IllegalStateException("The [blogDao] property must be set."); } } } |
1 2 3 4 5 |
<beans default-init-method="init"> <bean id="blogService" class="com.foo.DefaultBlogService"> <property name="blogDao" ref="blogDao" /> </bean> </beans> |
최상위 <beans/> 엘리먼스 속성에 default-init-method 속성의 존재는 스프링 IoC 컨테이너가 빈의 init라는 메서드를 초기화 메서드 콜백으로 인식하도록 한다. 빈이 생성되고 구성되었을 때 빈 클래스가 이러한 메서드를 가지고 있다면 적절한 시기에 호출된다.
파괴 메서드 콜백도 비슷하게 최상위 <beans/> 요소에 default-destroy-method 속성을 사용함으로써 설정한다.(XML에서)
존재하는 빈 클래스가 관례와 다른 이름의 콜백 메서드를 이미 가지고 있다면 <bean/> 자체에 init-method and destroy-method 속성을 사용하는 메서드를 지정함으로써(XML에서) 기본값을 오버라이드 할 수 있다.
스프링 컨테이너는 설정된 초기화 콜백이 빈이 모든 의존성을 제공받을 후에 즉시 호출된다는 것을 보장한다. 그래서 초기화 콜백은 로우(raw) 빈 참조에서 호출된다. 로우(raw) 빈 참조는 AOP 인터셉터 등이 아직 빈에 적용되지 않았다는 의미이다. 타겟 빈이 우선적으로 완전히 생성되고 그 다음 AOP 프록시(예를 들면)가 인터셉터 체인과 함께 적용된다. 타겟빈과 프록시가 따로 정의되어 있다면 코드는 프록시를 우회해서 로우(raw) 타겟 빈과 상호작용할 수 있다. 이는 프록시/인터셉터를 가진 타겟빈의 라이프사이클과 결합될 수 있고 코드가 직접 로우(raw) 타겟 빈과 상호작용할 때 이상한 시맨틱을 남길 수 있기 때문에 init 메서드에 인터셉터들을 적용하는데 일관성이 없을 수 있다.
4.6.1.4 라이프사이클 메카니즘 조합하기
스프링 2.5처럼 빈의 라이프사이클 동작을 제어하는데 3가지 선택사항이 있다. InitializingBean과 DisposableBean 콜백 인터페이스. 커스텀 init()과 destroy() 메서드. @PostConstruct 와 @PreDestroy 어노테이션. 주어진 빈을 제어하는 이러한 메카니즘을 조합할 수 있다.
Note
하나의 빈에 여러가지 라이프사이클 메카니즘을 설정하고 각 메가니즘이 다른 메서드명으로 설정했다면 각각의 설정된 메서드는 다음 리스트의 순서로 실행된다. 하지만 이러한 라이프사이클 메카니즘 중같은 메서드명으로 설정한 것이 있다면(예를 들어 초기화 메서드는 init()) 이전 섹션에서 설명했듯이 그 메서드들은 동시에 실행된다.
같은 빈에 다른 초기화 메서드로 설정된 여러가지 라이프사이클 메카니즘들은 다음과 같이 호출된다.
- @PostConstruct 어노테이션이 붙은 메서드
- InitializingBean 콜백 인터페이스로 정의된 afterPropertiesSet()
- 커스텀 설정된 init() 메서드
파괴 메서드도 같은 순서로 호출된다.
- @PreDestroy 어노테이션이 붙은 메서드
- DisposableBean 콜백 인터페이스로 정의된 destroy()
- 커스텀 설정된 destroy() 메서드
4.6.1.5 시작(startup) 콜백과 종료(shutdown) 콜백
Lifecycle 인터페이스는 라이프사이클에 자신만의 요구사항이 있는 객체의 핵심 메서드를 정의한다. (예를 들어 어떤 백그라운드 프로세스의 시작과 종료)
1 2 3 4 5 |
public interface Lifecycle { void start(); void stop(); boolean isRunning(); } |
스프링이 관리하는 모든 객체는 이 인터페이스를 구현한다. 그 다음 ApplicationContext이 시작하고 종료될 때 컨텍스트에서 정의한 모든 라이프사이클 구현체에 단계적으로 이러한 메서드를 호출한다. 이는 LifecycleProcessor에 위임해서 수행한다.
1 2 3 4 |
public interface LifecycleProcessor extends Lifecycle { void onRefresh(); void onClose(); } |
LifecycleProcessor는 Lifecycle의 확장이라는 것을 기억해야 한다. 컨텍스트를 갱신하고 닫는 행위를 하는 두가지 메서드를 추가했다.
시작과 종료의 호출 순서는 중요하다. 어떤 두 객체사이에 "의존하는" 관계가 있다면 의존하는 쪽이 의존성 다음에 시작될 것이고 의존성보다 먼저 종료될 것이다. 하지만 때때로 직접적인 의존성을 알지 못한다. 아마도 특정 타입의 객체가 다른 타입의 객체보다 먼저 시작되어야 한다는 것만 알 것이다. 이러한 경우에 SmartLifecycle 인터페이스는 다른 선택사항으로 부모 인터페이스 Phased에서 정의된 getPhase()를 정의한다.
1 2 3 4 5 6 7 8 |
public interface Phased { int getPhase(); }
public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback); } |
시작할 때는 가장 낮은 단계의 객체들이 먼저 시작되고 멈출 때는 반대 순서로 멈춘다. 그러므로 SmartLifecycle를 구현하고 Integer.MIN_VALUE를 리턴하는 getPhase()메서드가 있는 객체가 먼저 시작하고 나중에 멈출 것이다. 반대로 Integer.MAX_VALUE의 단계 값(phase value)은 객체가 나중에 시작되고 먼저 멈추어야 한다는 것을 가르킬 것이다.(실행하려는 다른 프로세스에 의존하고 있는 이유와 마찬가지로) 단계 값을 고려할 때 SmartLifecycle를 구현하지 않은 "보통"의 모든 Lifecycle 객체는 기본 단계가 0이 된다는 것도 알고 있어야 한다. 그러므로 단계 값이 음수이면 객체가 표준 컴포넌트전에 시작되어야 한다는 것 (표준 컴포넌트 다음에 종료된다.)을 가리키고 단계값이 양수라면 그 반대이다.
SmartLifecycle에 정의된 stop 메서드가 콜백을 받는 것을 볼 수 있듯이 모든 구현체는 구현체의 종료과정이 끝난후에 콜백의 run() 메서드를 반드시 호출해야 한다. 이는 LifecycleProcessor 인터페이스의 기본 구현체인 DefaultLifecycleProcessor가 콜백을 호출하기 위해 각 단계 내 객체그룹의 타임아웃 값까지 기다리기 때문에 필수적인 비동기 종료를 가능케한다. 단계마다 기본 타임아웃은 30초이다. 컨텍스트에서 "lifecycleProcessor"라는 이름의 빈을 정의함으로써 기본 라이프사이클 프로세서 인스턴스를 오버라이드할 수 있다. 타임아웃만 바꾸려면 다음과 같이 정의하는 것으로 충분할 것이다.
1 2 3 4 |
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor"> <!-- timeout value in milliseconds --> <property name="timeoutPerShutdownPhase" value="10000"/> </bean> |
말했듯이 LifecycleProcessor 인터페이스는 컨텍스트를 갱신하고 닫기위한 콜백 메서드를 정의한다. 후자는 stop()을 명시적으로 호출하는 것으로 간단히 종료과정을 진행할 수 있지만 이는 컨텍스트가 닫힐 때 진행될 것이다. 반면에 'refresh' 콜백은 SmartLifecycle 빈의 또 다른 기능을 사용하게 한다. 컨텍스트가 갱신되었을 때(모든 객체가 인스턴스화되고 초기화된 다음에) 갱신 콜백이 호출될 것이고 이 때 기본 라이프사이클 프로세서가 각 SmartLifecycle 객체의 isAutoStartup() 메서드가 리턴하는 boolean 값을 확인할 것이다. 만약 이 값이 "true"이면 그 객체는 컨텍스트의 명시적인 호출이나 자신의 start() 메서드를 기다리는 대신에 바로 시작할 것이다. (컨텍스트 갱신과는 다르게 컨텍스트 시작은 표준 컨텍스트 구현체를 위해서 자동적으로 일어나지 않는다.) "의존하는" 관계뿐만 아니라 "단계" 값도 위에서 설명한 것과 같은 방법으로 시작순서를 결정할 것이다.
4.6.1.6 웹 어플리케이션이 아닌 경우에 스프링 IoC 컨테이너를 안정적으로(gracefully) 종료하기
Note
이 섹션은 웹 어플리케이션이 아닌 경우에만 적용된다. 스프링의 웹기반 ApplicationContext에는 관련된 웹 어플리이션이 종료될 때 스프링 IoC 컨테이너를 안정적으로(gracefully) 종료하는 코드가 이미 있다.
웹 어플리케이션이 아닌 환경에서 스프링 IoC 컨테이너를 사용한다면(예를 들어 리치클라이언트 데스크탑 환경이라든지) JVM에 종료 훅(shutdown hook)을 등록한다. 종료 훅을 등록함으로써 안정적인 종료를 보장하고 모든 리소스를 릴리즈하기 위해 싱글톤 빈의 적절한 파괴 메서드를 호출한다. 물론 여전히 이러한 파괴 콜백을 적절하게 설정하고 구현해야 한다.
AbstractApplicationContext 클래스에 선언된 registerShutdownHook() 메서드를 호출해서 종료 훅을 등록한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(new String []{"beans.xml"});
// 위의 컨텍스트에 종료 훅(shutdown hook)을 추가한다... ctx.registerShutdownHook();
// 어플리케이션은 여기서 실행한다...
// main 메서드가 있고 훅(hook)은 어플리케이션을 종료보다 먼저 호출된다... } } |
4.6.2 ApplicationContextAware과 BeanNameAware
ApplicationContext이 org.springframework.context.ApplicationContextAware 인터페이스를 구현한 클래스를 생성할 때 클래스는 ApplicationContext에 대한 참조를 제공받는다.
1 2 3 |
public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; } |
그러므로 빈이 빈을 생성한 ApplicationContext를 프로그래밍적으로 다룰 수 있다. 이는 ApplicationContext 인터페이스를 통하거나 추가적인 기능을 노출하는 ConfigurableApplicationContext같은 인터페이스의 알려진 서브클래스에 대한 참조를 캐스팅함으로써 이뤄진다. 프로그래밍적으로 다른 빈을 복구하는 것이 한가지 사용이 될 것이다. 종종 이 기능은 유용하지만 보통은 사용하지 말아야 한다. 이 기능을 사용하면 코드가 스프링에 의존하게 되고 빈의 프로퍼티로 협력객체를 제공하는 제어의 역전 스타일을 따르지 않기 때문이다. ApplicationContext의 다른 메서드들은 파일 리소스, 어플리케이션 이벤트의 퍼블리싱, MessageSource 접근을 제공한다. 이러한 추가적인 기능은 Section 4.14, “Additional Capabilities of the ApplicationContext”에서 설명한다.
스프링 2.5처럼 자동 연결(autowiring)은 ApplicationContext의 참조를 얻는 또다른 대안이다. "전통적인" constructor와 byType 자동연결 모드(Section 4.4.5, “협력객체의 자동연결(Autowiring)”에서 설명했다.)는 각각 생성자 아규먼트나 setter 메서드 파라미터에 대한 ApplicationContext 타입 의존성을 제공할 수 있다. 필드들과 여러 파라미터를 가진 메서드들을 자동연결을 포함해서 더 유연하게 사용하려면 새로운 어노테이션 기반의 자동연결 기능을 사용해라. 이 기능을 사용하면 필드나 생성자나 메서드에 @Autowired 어노테이션을 사용했을 때 기대하는 BeanFactory 타입인 필드나 생성자 아규먼트나 메서드 파라미터에 ApplicationFactory이 자동연결된다. 더 자세한 내용은 Section 4.9.2, “@Autowired”를 참고해라.
ApplicationContext가 org.springframework.beans.factory.BeanNameAware 인터페이스를 구현한 클래스를 생성할 때 이 클래스는 관련된 객체 정의에서 정의한 이름에 대한 참조를 제공받는다.
1 2 3 |
public interface BeanNameAware { void setBeanName(string name) throws BeansException; } |
콜백은 보통의 빈 프로퍼티 그룹 후에 호출되지만 InitializingBean afterPropertiesSet나 커스텀 init-method같은 초기화 콜백보다는 먼저 호출된다.
4.6.3 그 밖의 Aware 인터페이스
앞에서 얘기한 ApplicationContextAware와 BeanNameAware 외에도 스프링은 빈이 필요로 하는 인프라적인 의존성을 빈이 컨테이너에 알릴 수 있도록하는 Aware 인터페이스들을 제공한다. 가장 중요한 Aware 인터페이스는 아래에 정리해 놨다. 일반적인 규칙에 따라 이름은 의존성의 타입을 잘 나타낸다.
Table 4.4. Aware interfaces
이름 |
주입되는 의존성 |
설명되어 있는 위치 |
ApplicationContextAware |
ApplicationContext의 선언 |
Section 4.6.2, “ApplicationContextAware과 BeanNameAware" |
ApplicationEventPublisherAware |
제공된 ApplicationContext의 이벤트 퍼블리셔 |
Section 4.14, "Additional Capabilities of the ApplicationContext" |
BeanClassLoaderAware |
빈(bean) 클래스들을 로드하는 클래스 로더 |
Section 4.3.2, "빈의 인스턴스화" |
BeanFactoryAware |
BeanFactory의 선언 |
Section 4.6.2, "ApplicationContextAware과 BeanNameAware" |
BeanNameAware |
선언한 빈의 이름 |
Section 4.6.2, "ApplicationContextAware과 BeanNameAware" |
BootstrapContextAware |
컨테이너가 동작하는 리소스 아답터BootstrapContext. 보통 JCA 친화적인ApplicationContext에서만 사용할 수 있다. |
Chapter 24, JCA CCI |
LoadTimeWeaverAware |
로딩시에 클래스 정의를 처리하려고 정의한 weaver |
Section 8.8.4, "Load-time weaving with AspectJ in the Spring Framework" |
MessageSourceAware |
메시지(파라미터화와 국제화 지원을 포함해서)를 처리하려고 설정한 전략 |
Section 4.14, "Additional Capabilities of the ApplicationContext" |
NotificationPublisherAware |
스프링의 JMX 노티피케이션 퍼블리셔 |
Section 23.7, "Notifications" |
PortletConfigAware |
컨테이너가 동작하는 현재PortletConfig. 웹 친화적인 스프링의ApplicationContext에서만 유효하다. |
Chapter 19, Portlet MVC Framework |
PortletContextAware |
컨테이너가 동작하는 현재PortletContext. 웹 친화적인 스프링의ApplicationContext에서만 유효하다. |
Chapter 19, Portlet MVC Framework |
ResourceLoaderAware |
저레벨 리소스접근을 위해 설정한 로더 |
Chapter 5, Resources |
ServletConfigAware |
컨테이너가 동작하는 현재ServletConfig. 웹 친화적인 스프링의ApplicationContext에서만 유효하다. |
Chapter 16, Web MVC framework |
ServletContextAware |
컨테이너가 동작하는 현재ServletContext. 웹 친화적인 스프링의ApplicationContext에서만 유효하다. |
Chapter 16, Web MVC framework |
이러한 인터페이스의 사용은 코드를 스프링 API의 의존하게 하고 제어의 역전 스타일을 따르지 않는다는 것을 다시 강조한다. 이처럼 컨테이너에 프로그래밍적으로 접근할 필요가 있는 인프라적인 빈에 사용하기를 권장한다.
4.7 빈 정의 상속
빈 정의는 초기화 메서드, 정적 팩토리 메서드명 등과 같은 컨테이너 특유의 정보, 생성자 아규먼트, 프로퍼티 값을 포함해서 많은 설정정보를 담고 있다. 자식 빈 정의는 부모 정의에서 설정 데이터를 상속받는다. 자식 빈은 값을 오버라이드할 수 있고 필요하다면 추가할 수도 있다. 부모 빈 정의와 자식 빈 정의를 사용하면 타이핑 양을 많이 줄일 수 있다. 사실상 이는 템플릿 형식이다.
ApplicationContext 인터페이스를 프로그래밍적으로 작업한다면 자식 빈 정의는 ChildBeanDefinition 클래스로 나타난다. 대부분의 사용자는 이러한 레벨로 작업하지 않고 대신에 ClassPathXmlApplicationContext같은 어떤 선언적인 빈정의로 설정한다. XML 기반의 설정 메타데이터를 사용한다면 parent 속성의 값으로 부모 빈을 지정해서 자식 빈 정의를 나타낸다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean>
<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/> <!-- age 프로퍼티 값 1은 부모로부터 상속될 것이다. --> </bean> |
지정한 것이 아무것도 없다면 자식 빈 정의는 부모 정의에서 빈 클래스를 사용하고 자식 빈 정의는 이를 오버라이드할 수 있다. 오버라이드할 경우 자식 빈 클래스는 부모와 반드시 호환성이 있어야 한다. 즉 자식 빈 클래스는 부모의 프로퍼티 값을 받아야 한다.
자식 빈 정의는 부모로부터 생성자 아규먼트 값, 프로퍼티 값, 메서드 오버라이드를 상속받고 새로운 값을 추가할 수도 있다. 지정한 어떤 초기화 메서드나 파괴 메서드 static 팩토리 메서드 설정도 대응되는 부모의 설정을 오버라이드 할 것이다.
남은 설정은 항상 자식 정의에서 가져온다: 의존성, 자동연결 모드, 의존성 확인, 싱글톤, 범위, 지연 초기화.
앞의 예제는 abstract 송석을 사용해서 명시적으로 부모 빈 정의를 추상으로 표시했다. 부모 정의가 클래스를 지정하지 않았다면 다음과 같이 명시적으로 부모 빈 정의를 abstract로 표시해야한다.
1 2 3 4 5 6 7 8 9 10 |
<bean id="inheritedTestBeanWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize"> <property name="name" value="override"/> <!-- age는 부모 빈 정의에서 1의 값을 상속받을 것이다--> </bean> |
부모 빈은 미완성이기 때문에 그 자체로는 초기화 될 수 없고 abstract로 명시되어 있다. 이처럼 정의가 abstract이면 자식정의를 위해서 부모정의가 제공하는 빈 정의 템플릿으로서만 사용할 수 있다. 다른 빈의 ref 프로퍼티로 참조하거나 명시적으로 부모 빈의 id로 getBean()호출해서 abstract인 부모 빈 자체를 사용하려고 하면 오류가 발생한다. 유사하게 컨테이너 내부의 preInstantiateSingletons() 메서드는 추상으로 정의된 빈 정의를 무시한다.
Note
ApplicationContext는 기본적으로 모든 싱글톤을 미리 인스턴스화한다. 그래서 템플릿으로만 사용하려는 (부모)빈 정의가 있고 이 빈 정의가 클래스를 지정하고 있다면 모든 싱글톤이 미리 인스턴스화된다는 사실은 중요하다.(최소한 싱글톤 빈에 대해서는) 반드시 abstract 속성을 true로 설정했는지 확인해야 한다. 그렇지 않으면 어플리케이션 컨텍스트는 실제로 abstract 빈을 미리 인스턴스화하려고 시도할 것이다.
'개발 > Spring' 카테고리의 다른 글
IoC 컨테이너 9장 (0) | 2015.08.18 |
---|---|
IoC 컨테이너 8장 (0) | 2015.08.18 |
IoC 컨테이너 6장 (0) | 2015.08.18 |
IoC 컨테이너 5장 (0) | 2015.08.18 |
IoC 컨테이너 4장 (0) | 2015.08.18 |