Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

[Spring 레퍼런스] 4장 IoC 컨테이너 #6

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.




4.5 빈(Bean) 범위
빈 정의를 생성할 때 빈 정의로 정의한 클래스의 실제 인스턴스를 생성하기 위해 레시피를 만든다. 클래스처럼 하나의 레시피에서 많은 객체 인스턴스를 생성할 수 있다는 것을 의미하기 때문에 빈 정의가 레시피라는 개념은 중요하다.

특정 빈 정의로 생성한 객체에 연결할 다양한 의존성과 설정값뿐만 아니라 생성된 객체의 범위도 제어할 수 있다. 이 접근방법은 자바 클래스 레벨에서 객체의 범위를 생성하는 대신에 설정을 통해서 생성한 객체들의 범위를 선택할 수 있으므로 강력하고 유연하다. 빈을 여러 가지 범위 중 하나로 정의할 수 있다. 스프링 프레임워크는 다섯가지 범위를 지원한다. 이 중 3가지는 웹기반의 ApplicationContext에서만 사용할 수 있다.

다음과 같은 범위들이 준비되어 있다. 커스텀 범위를 만들 수도 있다.

Table 4.3. 빈의 범위
범위설명

singleton

(기본값) 하나의 빈정의가 스프링 IoC 컨테이너마다 하나의 객체 인스턴스가 되는 범위

prototype

하나의 빈 정의가 다수의 객체 인스턴스가 되는 범위

request

하나의 빈 정의가 하나의 HTTP 요청의 라이프사이클이 되는 범위; 이는 각 HTTP 요청은 빈 정의로 부터 생성된 자신만의 빈 인스턴스를 갖는다. 웹 친화적인 스프링ApplicationContext의 컨텍스트에서만 유효하다.

session

하나의 빈 정의가 하나의 HTTP Session의 라이프사이클이 되는 범위. 웹 친화적인 스프링  ApplicationContext의 컨텍스트에서만 유효하다.

global session

하나의 빈 정의가 전역적인 HTTP Session의 라이프사이클이 되는 범위. 보통 포틀릿(portlet) 컨텍스트내에서 사용할 때만 유효하다. 웹 친화적인 스프링ApplicationContext의 컨텍스트에서만 유효하다.


스레드의 범위를 갖는 빈
스프링 3.0에서는 스레드 범위를 사용할 수 있지만 기본적으로는 등록되어 있지 않다. 더 자세한 내용은 SimpleThreadScope를 봐라. 스레드 범위나 다른 커스텀 범위를 등록하는 방법을 알고 싶다면 Section 4.5.5.2, “커스텀 범위의 사용”를 봐라.



4.5.1 싱글톤 범위
싱글톤 빈의 유일하고 공유된 인스턴스로 관리되고 해당 빈으로 매칭되는 id의 빈의 모든 요청은 스프링 컨테이너가 같은 빈 인스턴스를 돌려준다.

간단히 말해서 빈 정의를 싱글톤 범위로 정의하면 스프링 IoC 컨테이너는 해당 빈 정의로 정의된 객체의 정확히 하나의 인스턴스를 생성한다. 이 하나의 인스턴스는 싱글톤 빈 같은 캐시로 저장하고 해당 빈에 대한 이후의 모든 요청이나 참조에 캐시된 객체를 돌려준다.

사용자 삽입 이미지

스프링의 싱글톤 빈의 개념은 Gang of Four (GoF)의 패턴북에 나온 싱글톤 패턴과는 다르다. GoF 싱글톤은 특정 클래스에 대해서 ClassLoader 마다 유일한 인스턴스가 생성되는 것처럼 객체의 범위를 하드코딩한다. 스프링 싱글톤의 범위는 컨테이너 마다, 빈 마다 라는 것이 가장 좋은 설명이다. 이 말은 하나의 스프링 컨테이너에서 특정 클래스의 빈을 정의하면 스프링 컨테이너는 빈 정의의 클래스에 대한 유일한 인스턴스를 생성한다. 싱글톤 범위는 스프링의 기본범위이다. XML에서 싱글톤으로 빈을 정의하려면 다음과 같이 작성한다.


<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- 장황하긴 하지만 다음처럼 해도 똑같다. (싱글톤 범위는 기본값이다) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>




4.5.2 프로토타입 범위

싱글톤이 아닌 프로토타입 범위의 빈 배포는 특정한 빈에 대한 요청마다 새로운 빈(bean) 인스턴스를 생성한다. 이는 빈이 다른 빈에 주입되거나 컨테이너에 getBean() 메서드를 호출해서 빈을 요청하는 것을 의미한다. 규칙에 따라 상태를 가진 모든 빈에는 프로토타입 범위를 사용하고 상태가 없는 빈에는 싱글톤 범위를 사용해라.

다음 다이어그램은 스프링의 프로토타입 범위를 나타낸다. 전형적인 DAO는 주고받는 어떤 상태도 가지지 않기 때문에 데이터 접근 객체 (DAO)는 보통 프로토타입으로 설정하지 않는다.

사용자 삽입 이미지

다음은 XML에서 빈을 프로토타입으로 정의하는 예제이다.


<!-- spring-beans-2.0.dtd의 사용 -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>



다름 범위와는 대조적으로 스프링은 프로토타입 빈의 완전한 라이프사이클을 관리하지 않는다. 컨테이너는 프로토타입 객체를 인스턴스화하고 설정하고 다른 방법으로 모은 다음에 프로토타입 인스턴스의 어떤 기록도 없이 클라이언트에 전달한다. 그러므로 초기화 라이프사이클 콜백 메서드가 범위와 관계없이 모든 객체에서 호출되었더라고 설정된 소멸 라이프사이클 콜백은 호출되지 않는다. 클라이언트 코드는 반드시 프로토타입 범위를 가진 객체를 정리해야 하고 프로토타입 빈이 잡고 있는 비싼 리소스들을 해제시켜야 한다. 스프링 컨테이너가 프로토타입 범위를 가진 빈이 붙잡고 있는 리소스를 해제하도록 하려면 정리되어야 하는 빈의 참조를 가진 커스텀 bean post-processor를 사용해 봐라.

어떤 관점에서는 프로토타입 범위를 가진 빈에 관한 스프링 컨테이너의 역할이 자바의 new 오퍼레이터 대신이다. 그 관점에서 과거의 모든 라이프사이클 관리는 클라이언트에서 처리해야 합니다. (스프링 컨테이너의 빈 라이프사이클에 대한 자세한 내용은 Section 4.6.1, “라이프사이클 콜백”를 봐라.)



4.5.3 프로토타입 빈에 의존성이 있는 싱글톤 빈

프로토타입 빈에 의존성이 있는 싱글톤 범위의 빈을 사용하면 인스턴스화 할 때 의존성이 처리된다는 것을 알아야 한다. 그러므로 싱글톤 범위의 빈에 프로토타입 범위의 빈을 의존성 주입하면 새로운 프로토타입 빈을 인스턴스화 한 후 싱글톤 빈에 의존성 주입한다. 이 프로토타입 인스턴스는 싱글톤 범위의 빈에 항상 제공되는 유일한 인스턴스이다.

하지만 싱글톤 범위의 빈이 런타임시에 계속해서 프로토타입 범위를 가진 빈의 새로운 인스턴스를 얻어야 하는 경우를 생각해 보자. 의존성 주입은 스프링 컨테이너가 싱글톤 빈을 인스턴스화하고 빈의 의존성을 처리하고 주입할 때만 딱 한번 일어나므로 싱글톤 빈에 프로토타입 범위의 빈을 의존성 주입할 수 없다. 런타임에서 프로토타입 빈의 새로운 인스턴스가 하나이상 필요하다면 Section 4.4.6, “메서드 주입”를 참고해라.



4.5.4 리퀘스트, 세션, 글로벌 세션 범위
request, session, global session 범위는 웹기반 스프링 ApplicationContext 구현에서만 사용할 수 있다. 이러한 범위들을 ClassPathXmlApplicationContext같은 보통의 스프링 IoC 컨테이너와 함께 사용한다면 모르는 빈 범위라는 의미로 IllegalStateException이 발생한다.


4.5.4.1 초기 웹 설정
request, session, global session 레벨(웹 범위의 빈)에서 빈의 범위를 지원하려면 빈을 정의하기 전에 몇가지 마이너한 초기 설정을 해야한다. (이 초기 설정은 표준 범위인 싱글톤과 프로토타입 범위에서는 필요하지 않다.)

특정한 서블릿 환경에 기반해서 이 초기 설정을 어떻게 해야하는가..

스프링 웹 MVC 안에서(사실상은 요청안에서) 범위를 가진 빈에 접근하면 스프링 DispatcherServlet나 DispatcherPortlet이 처리하고 특별한 설정은 필요하지 않다. DispatcherServlet와 DispatcherPortlet는 이미 모든 연관된 상태를 드러낸다.

스프링의 DispatcherServlet 밖에서 처리되는 요청이 있는 서블릿 2.4이상의 웹 컨테이너를 사용한다면(예를 들어 JSF나 스트럿츠(Struts)를 사용하는 경우) 웹 어플리케이션의 web.xml 파일에 다음 javax.servlet.ServletRequestListener 선언을 추가해야 한다.


<web-app>
...
<listener>
  <listener-class>
      org.springframework.web.context.request.RequestContextListener
  </listener-class>
</listener>
...
</web-app>



더 오래된 웹 컨테이너 (Servlet 2.3)를 사용한다면 제공되는 javax.servlet.Filter 구형체를 사용해라. 서블릿 2.3 컨테이너에서 스프링의 DispatcherServlet 밖의 요청에서 웹 범위를 가진 빈에 접근하려면 웹 어플리케이션의 web.xml 파일에 다음의 XML 설정을 포함시켜야 한다.(필터는 웹 어플리케이션 환경에 기반에서 매핑되므로 웹 어플리케이션 환경을 적절하게 수정해야 한다.)


<web-app>
..
<filter>
  <filter-name>requestContextFilter</filter-name>
  <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>requestContextFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>




DispatcherServlet, RequestContextListener, RequestContextFilter는 모두 완전히 같은 일을 한다. 즉, HTTP 요청 객체를 해당 요청을 서비스하는 Thread에 바인딩한다. 이는 요청이나 세션범위의 빈을 콜 체인(call chain)에서 이용가능하도록 한다.


4.5.4.2 요청 범위(Request scope)

다음 빈 정의를 보자.


<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>



스프링 컨테이너는 모든 HTTP 요청에 대해서 loginAction 빈 정의를 사용해서 LoginAction 빈의 새로운 인스턴스를 생성한다. loginAction빈은 HTTP 요청 레벨의 범위가 된다. 같은 loginAction 빈 정의로 생성된 다른 인스턴스들은 이러한 상태변화를 볼 수 없기 때문에 생성된 인스턴스의 내부 상태를 원하는 대로 수정할 수 있다. 인스턴스들은 각 요청에 개별적이다. 요청의 처리가 완료되었을 때 요청 범위의 빈은 버려진다.


4.5.4.3 세션 범위(Session scope)
다음 빈 정의를 보자.


<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

스프링 컨테이너는 단일 HTTP Session의 생명주기에 대해서 userPreferences 빈 정의를 사용해서 UserPreferences 빈의 새로운 인스턴스를 생성한다. 다시 말하면 userPreferences 빈은 HTTP Session 레벨에서 유효한 범위가 된다. 요청 범위의 빈처럼 생성된 인스턴스의 내부 상태를 원하는 만큼 바꿀 수 있다. 같은 userPreferences 빈 정의에서 생성된 인스턴스를 사용하는 다른 HTTP Session 인스턴스는 개별적인 HTTP Session을 갖기 때문에 이러한 상태변화를 볼 수 없다. HTTP Session이 버려졌을 때 특정 HTTP Session의 범위인 빈도 역시 버려진다.


4.5.4.4 전역 세션 변위(Global session scope)
다음 빈 정의를 보자.


<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>

전역 세션 범위는 표준 HTTP Session 범위(위에서 설명했다)와 유사하고 포틀릿 기반의 웹 어플리케이션의 컨텍스트에서만 적용된다. 포틀릿 명세는 Session 세션의 개념을 단일 포틀릿 웹 어플리케이션의 모든 포틀릿 가운데 공유된다고 정의한다. 전역 세션 범위로 정의된 빈은 전역 포틀릿의 생명주기의 범위(또는 바인드)가 된다.

표준 서블릿 기반의 웹 어플리케이션을 작성하고 전역 세션 범위를 가지는 하나 이상의 빈을 정의하면 표준 HTTP Session 범위가 사용되고 에러가 발생하지 않는다.


4.5.4.5 의존성에서 범위를 가진 빈
스프링 IoC 컨테이너는 객체들(빈)의 인스턴스화 뿐만 아니라 협력 객체들(또는 의존성)들의 연결까지 관리한다. (예를 들어) HTTP 요청 범위의 빈은 다른 빈으로 주입하려고 하면 HTTP 요청 범위 빈의 위치에 AOP 프록시를 주입해야 한다. 이것은 같은 퍼블릭 인터페이스를 HTTP 요청 범위의 빈으로 노출하는 프록시 객체를 주입해야할 필요가 있다. 하지만 관계된 범위에서(예를 들면 HTTP 요청) 실제 타겟 객체를 획득하고 실제 객체에서 위임 메서드를 호출할 수 있다.

Note
싱글톤이나 프로토타입같은 범위를 가진 빈과의 결합을 위해서 <aop:scoped-proxy/>를 사용할 필요가 없다. 싱글톤 빈에 대한 범위를 가진 프록시를 생성하려고 시도하면 BeanCreationException이 발생한다.

다음 예제의 설정은 한 라인뿐이지만 내부에서 “어떻게” 동작하고 “왜” 그렇게 동작하는지 이해하는 데 중요하다.


<?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:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

  <!-- HTTP Session범위의 빈은 프록시로 노출된다 -->
  <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">

        <!-- 주변의 빈을 프록시하도록 컨테이너에게 지시한다 -->
        <aop:scoped-proxy/>
  </bean>

  <!-- 싱글톤 범위의 빈이 위의 빈에 대한 프록시에 주입된다 -->
  <bean id="userService" class="com.foo.SimpleUserService">

      <!-- 프롯시된 userPreferences 빈에 대한 참조 -->
      <property name="userPreferences" ref="userPreferences"/>

  </bean>
</beans>


이러한 프록시를 생성하려면 범위를 가진 빈 정의의 자식요소로 <aop:scoped-proxy/> 요소를 추가해야 한다. (클래스 기반의 프록싱을 선택하다면 클래스 패스에 CGLIB 라이브러리도 추가해야 한다. the section called “생성할 프록시의 타입 선택”와 Appendix C, XML Schema-based configuration를 참고해라.) request, session, globalSession, 커스텀 레벨의 범위를 가진 빈 정의는 왜 <aop:scoped-proxy/> 요소가 필요한가? 다음 싱글톤 빈 정의를 시도해보고 앞에서 언급한 범위에 대해서 무엇을 정의해야 하는가와 비교해 봐라. (다음 userPreferences 빈 정의로는 불완전하다.)


<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

<bean id="userManager" class="com.foo.UserManager">
  <property name="userPreferences" ref="userPreferences"/>
</bean>



앞의 예제에서 싱글톤 빈 userManager는 HTTP Session 범위의 빈 userPreferences에 대한 참조와 함께 주입된다. 여기서 두드러진 점은 userManager 빈이 싱글톤 이라는 것이다. 이는 컨테이너 당 딱 한번만 인스턴스화 되고 그 의존성(이 경우에서는 유일한 userPreferences 빈)도 역시 한번만 주입된다. 이는 userManager 빈은 최초에 함께 주입된 완전히 같은 userPreferences 객체상에서만 수행된다는 것을 의미한다.

생명이 긴 범위의 빈에 생명이 짧은 범위의 빈을 주입할 때 이 동작은 원하는 동작이 아니다. 예를 들어 HTTP Session 범위의 협력 빈을 의존성으로 싱글톤 빈에 주입하는 경우이다. 더 정확히 얘기하자면 HTTP Session 의 생명주기인 단일 userManager 객체가 필요하고 앞에서 얘기한 HTTP Session을 가리키는 userPreferences 객체가 필요하다. 그러므로 컨테이너는 스코프 메카니즘(HTTP 요청, Session 등등)에서 실제 UserPreferences 객체를 가져올 수 있는 UserPreferences 클래스처럼 완전히 같은 퍼블릭 인터페이스를 노출하는 객체를 생성한다. (원칙적으로 객체는 UserPreferences 인스턴스 이다.) 컨테이너는 이 프록시 객체를 userManager에 주입하고 userManager는 이 UserPreferences 참조가 프록시라는 것을 모르고 있다. 이 예제에서 UserManager 인스턴스는 의존성 주입된 UserPreferences 객체의 메서드를 호출했을 때 사실상 프록시의 메서드를 호출한다. 그 다음 프록시는 (이 경우에는) HTTP Session에서 실제 UserPreferences 객체를 가져오고 메서드 호출을 획득한 실제 UserPreferences 객체에 위임한다.

그러므로 요청, 세션, 전역세션 범위의 빈을 협력객체에 주입하려면 다음의 제대로된 완전한 설정이 필요하다.


<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
  <aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.foo.UserManager">
  <property name="userPreferences" ref="userPreferences"/>
</bean>



생성할 프록시의 타입 선택
기본적으로 스프링 컨테이너가 <aop:scoped-proxy/> 요소로 표시된 빈에 대한 프록시를 생성할 때 CGLIB 기반 클래스 프록시를 생성한다. 이 말은 어플리케이션 클래스 패스에 CGLIB 라이브러리가 필요하다는 의미이다.

Note: CGLIB 프록시는 퍼블릭 메서드 호출만 가로챈다! 이러한 프록시에서 퍼블릭이 아닌 메서드를 호출하지 마라. 퍼블릭이 아닌 메서드는 범위를 가진 타겟 객체로 위임되지 않을 것이다.

대신 이러한 범위의 빈에 대해서 <aop:scoped-proxy/> 요소의 proxy-target-class 속성의 값을 false로 지정함으로써 스프링 컨테이너가 표준 JDK 인터페이스 기반의 프록시를 생성하도록 설정할 수 있다. JDK 인터페이스 기반 프록시를 사용한다는 것은 이러한 프록시가 유효하도록 하기 위해 어플리케이션 클래스패스에 추가적인 라이브러리가 필요없다는 것을 의미한다. 하지만 이러한 범위를 가진 빈의 클래스가 반드시 최소한 하나의 인터페이스를 구현해야 한다는 것과 이러한 범위를 가진 빈으로 주입된 모든 협력객체들은 반드시 인터페이스 중 하나를 통해서 빈을 참조해야 한다는 것을 의미한다.


<!-- DefaultUserPreferences는 UserPreferences 인터페이스를 구현한다 -->
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
  <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.foo.UserManager">
  <property name="userPreferences" ref="userPreferences"/>
</bean>



클래스 기반이나 인터페이스 기반의 프록시 선택에 대한 더 자세한 내용은 Section 8.6, “Proxying mechanisms”를 참조해라.



4.5.5 커스텀 범위
스프링 2.0처럼 빈 범위의 메카니즘은 확장할 수 있다. 자신만의 범위를 정의하고나 이미 존재하는 범위를 재정의 할 수도 있다. 하지만 기존의 범위를 재정의하는 것은 별로 권장하지 않으며 내장된 singleton과 prototype 범위는 오버라이드 할 수 없다.


4.5.5.1 커스텀 범위 생성
스프링 컨테이너와 커스텀 범위를 통합하려면 이번 섹션에서 설명할 org.springframework.beans.factory.config.Scope 인터페이스를 구현해야 한다. 어떻게 자신의 범위를 구현하는가에 대한 개념을 알고 싶다면 스프링 프레임워크가 제공하는 Scope 구현을 참고하고 구현해야 하는 메서드에 대해서 자세히 설명해 놓은 Scope Javadoc을 참고해라.

Scope 인터페이스는 범위에서 객체를 얻고, 범위에서 객체를 제거하고 소멸되도록 하는 4가지 함수가 있다.

다음 메서드는 의존하는 범위에서 객체를 리턴한다. 예를 들어 세션 범위 구현은 세션 범위의 빈을 리턴한다. (그리고 존재하지 않는다면 메서드는 차후 참조를 위해서 빈을 바인딩한 후 새로운 인스턴스를 리턴한다.)

Object get(String name, ObjectFactory objectFactory)

다음 메서드는 의존하는 범위에서 객체를 제거한다. 예를 들어 세션 범위 구현은 의존하는 세션에서 세션 범위의 빈을 제거한다. 객체는 리턴되어야 하지만 지정한 이름의 빈이 없다면 null을 리턴할 수 있다.

Object remove(String name)

다음 메서드는 범위가 소멸되거나 범위내에서 지정한 객체가 소멸될 때 실행되어야 하는 콜백을 등록한다. 소멸 콜백에 대한 더 자세한 내용은 Javadoc을 참조하거나 스프링 범위 구현체를 참조해라.

void registerDestructionCallback(String name, Runnable destructionCallback)

다음 메서드는 의존하는 범위에 대한 conversation 식별자를 획득한다. 이 식별자는 각 범위마다 달르다. 세션 범위의 구형체에서 이 식별자는 세션 식별자가 될 수 있다.

String getConversationId()



4.5.5.2 커스텀 범위의 사용

하나 이상의 커스텀 Scope 구현체를 작성하고 테스트한 후 스프링 컨테이너가 작성한 새로운 범위를 익식하도록 해야한다. 다음 메서드는 새로운 Scope를 스프링 컨테이너에 등록하는 핵심 메서드이다.

void registerScope(String scopeName, Scope scope);


이 메서드는 ConfigurableBeanFactory 인터페이스에 선언되어 있다. ConfigurableBeanFactory 인터페이스는 BeanFactory 프로퍼티로 스프링과 함께 제공되는 대부분의 ApplicationContext 구현체에서 사용할 수 있다.

registerScope(..)의 첫번째 아규먼트는 범위와 연관되는 유일한 이름이다. singleton나 prototype이 스프링내에서 사용하는 이러한 이름의 예이다. registerScope(..) 메서드의 두번재 아규먼트는 등록하고 사용할 커스텀 Scope 구현체의 실제 인스턴스다.

자신만의 커스텀 Scope 구현체를 작성한다면 다음과 같이 등록한다.

Note
다음 예제는 스프링에 포함된 SimpleThreadScope를 사용하지만 기본적으로 등록되지는 않는다. 이 설명은 자신만의 커스텀 Scope 구현체에서도 똑같이 적용될 것이다.


Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);


그 다음 커스텀 Scope의 범위 규칙을 따르는 빈 정의를 생성한다.

<bean id="..." class="..." scope="thread">

커스텀 Scope 구현체를 사용할 때 범위를 프로그래밍적으로 등록하는데 제한은 없다. 또한 CustomScopeConfigurer 클래스를 사용해서 선언적으로 Scope를 등록할 수도 있다.

<?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:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

  <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
      <property name="scopes">
          <map>
              <entry key="thread">
                  <bean class="org.springframework.context.support.SimpleThreadScope"/>
              </entry>
          </map>
      </property>
  </bean>

  <bean id="bar" class="x.y.Bar" scope="thread">
      <property name="name" value="Rick"/>
      <aop:scoped-proxy/>
  </bean>

  <bean id="foo" class="x.y.Foo">
      <property name="bar" ref="bar"/>
  </bean>

</beans>

Note
FactoryBean구현체에 <aop:scoped-proxy/>가 있으면 이는 getObject()에서 리턴받은 객체가 아닌 스스로 범위를 가진 팩토리 빈이다.

2012/03/29 01:53 2012/03/29 01:53