1.5. 빈의 범위 by sh

Bean을 정의할 때, 해당 Bean의 정의에 의해 정의된 클래스의 실제 인스턴스를 작성하기 위한 레시피를 작성합니다. Bean 정의가 레시피 라는 개념은 클래스와 마찬가지로 단일 레시피에서 여러 개의 객체 인스턴스를 생성 할 수 있기 때문에 중요합니다.

특정한 Bean의 정의로부터 생성된 객체에 플러그인되는 다양한 종속성 및 구성 값 뿐만 아니라 특정한 Bean의 정의로부터 생성된 객체의 범위를 제어 할 수 있습니다. 이러한 접근법은 강력하고 융통성이 있습니다. 자바 클래스 수준에서 객체의 범위에서 굽는 대신, 구성을 통해 만드는 객체에 대해 영역을 선택할 수 있기 때문입니다. Bean들은 여러 범위 중 하나에 배포되도록 정의 될 수 있습니다. 스프링 프레임 워크에서는 6개의 영역을(Scope) 지원하며, 그 중 4개의 영역은 웹 인식 ApplicationContext를 사용하는 경우에만 사용할 수 있습니다. 사용자 지정 영역을 만들 수도 있습니다.

다음 표는 지원되는 영역(Scope)을 설명합니다.

Table 3. Bean scopes

Scope

Description

하나의 Bean 정의에 대해서 스프링 IoC 컨테이너 내에 단 하나의 객체만 존재합니다. (기본 값)

하나의 Bean 정의에 대해서 다수의 객체가 존재할 수 있습니다.

하나의 Bean 정의에 대해서 하나의 HTTP Request 생명 주기 안에 단 하나의 객체만 존재합니다. 즉, 각각의 HTTP 요청은 자신만의 객체를 가집니다. 웹 인식 스프링 ApplicationContext의 컨텍스트에서만 유효합니다.

하나의 Bean 정의에 대해서 하나의 HTTP 세션 생명 주기 안에 단 하나의 객체만 존재합니다. 웹 인식 스프링 ApplicationContext의 컨텍스트에서만 유효합니다.

하나의 Bean 정의에 대해서 하나의 ServletContext 생명 주기 안에 단 하나의 객체만 존재합니다. 웹 인식 스프링 ApplicationContext의 컨텍스트에서만 유효합니다.

하나의 Bean 정의에 대해서 하나의 웹 소켓 생명 주기 안에 단 하나의 객체만 존재합니다. 웹 인식 스프링 ApplicationContext의 컨텍스트에서만 유효합니다.

Spring 3.0부터는 쓰레드 스코프를 사용할 수 있지만, 기본적으로 등록되어 있지 않습니다. 자세한 내용은 SimpleThreadScope 설명서를 참조하십시오. 이 또는 다른 사용자 지정 범위를 등록하는 방법에 대한 지침은 사용자 지정 범위 사용을 참조하십시오.

1.5.1. The Singleton Scope

하나의 싱글톤 Bean에 대한 공유된 하나의 인스턴스만 관리되며, 해당 Bean의 정의와 일치하는 ID를 가진 Bean에 대한 모든 요청은 그 하나의 특정 Bean의 인스턴스가 스프링 컨테이너에 의해 리턴되는 결과로 이어집니다.

다시 말해서, Bean의 정의를 정의하고 이것이 싱글톤으로 지정되면, 스프링 IoC 컨테이너는 정확히 그 Bean의 정의에 의해 정의된 하나의 객체 인스턴스를 생성합니다. 이 단일 인스턴스는 이러한 싱글톤 Bean의 캐시에 저장되며, 그 이후의 모든 요청과 이름 붙여진 Bean에 대한 참조는 캐시된 객체를 반환합니다. 다음 이미지는 싱글톤 스코프가 어떻게 동작하는 지를 설명합니다.

싱글톤 Bean에 대한 스프링의 개념은 GoF(Gang of Four) 패턴 책에 정의된 싱글톤 패턴과 다릅니다. GoF 싱글톤은 ClassLoader당 하나의 특정 클래스 인스턴스만 생성되도록 객체의 범위를 하드 코딩합니다. 스프링 싱글톤의 범위는 컨테이너 단위 및 Bean 단위로 잘 묘사됩니다. 즉, 단일 스프링 컨테이너에서 특정 클래스에 대해 하나의 Bean을 정의하면, 스프링 컨테이너는 해당 Bean의 정의로 정의된 클래스의 인스턴스 하나만 생성합니다. 싱글톤 범위는 스프링의 기본 범위입니다. XML에서 Bean을 하나의 싱글 톤으로 정의하려면, 다음 예처럼 Bean을 정의할 수 있습니다.

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

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

1.5.2. The Prototype Scope

Bean의 비 싱글톤인 프로토 타입 범위는 특정 Bean에 대한 요청이 이루어질 때마다 새로운 Bean 인스턴스를 생성합니다. 즉, Bean은 또 다른 Bean으로 주입되거나, 컨테이너에서 getBean() 메소드 호출을 통해 Bean을 요청합니다. 원칙적으로는, 모든 Stateful Bean에 대해 프로토타입 범위를, Stateless Bean에 대해 싱글톤 범위를 사용해야합니다.

다음 다이어그램은 스프링 프로토타입의 범위를 보여줍니다.

(일반적인 DAO는 대화 상태를 유지하지 않기 때문에, DAO(데이터 접근 객체)는 일반적으로 프로토 타입으로 구성되지 않습니다. 싱글톤 다이어그램의 핵심을 재사용하는 것이 더 쉽습니다)

다음의 예는 XML에서 프로토타입으로 Bean을 정의합니다.

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

다른 범위들과 달리, 스프링은 프로토 타입 Bean의 전체 라이프 사이클을 관리하지 않습니다. 컨테이너는 프로토 타입 객체를 인스턴스화, 구성 및 조합하고 그 프로토 타입 객체를 더 이상 조합하지 않고 클라이언트에게 전달합니다. 따라서, 초기화 라이프 사이클 콜백 메소드는 범위에 관계없이 모든 객체에서 호출되지만, 프로토 타입의 경우 구성된 라이프 사이클 콜백은 호출되지 않습니다. 클라이언트 코드는 프로토 타입 범위의 객체를 정리하고, 해당 프로토 타입 Bean이 보유하는 값비싼 리소스를 릴리즈 해야합니다. 스프링 컨테이너가 프로토 타입 범위의 Bean에 의해 보유된 리소스를 해제하게 하려면, 정리해야하는 Bean에 대한 참조를 가진 커스텀 Bean 포스트 프로세서를 사용해보십시오.

어떤 점에서, 프로토 타입 Bean과 관련된 컨테이너의 역할은 자바의 new 연산자를 대체합니다. 그 점을 지난 모든 라이프 사이클 관리는 클라이언트에 의해 처리되어야 합니다. (스프링 컨테이너에서의 Bean 라이프 사이클에 대한 자세한 내용은 라이프 사이클 콜백을 참조하십시오)

1.5.3. Singleton Beans with Prototype-bean Dependencies

프로토 타입 Bean에 대한 종속성을 가진 싱글톤 Bean을 사용할 때는, 인스턴스 화 시점에서 종속성이 처리된다는 점을 유의하십시오. 따라서, 프로토 타입 Bean을 싱글톤 Bean에 종속성으로 주입하면, 새로운 프로토 타입 Bean이 인스턴스화된 다음에 싱글톤 Bean으로 종속성이 주입됩니다. 프로토 타입 인스턴스는 싱글톤 Bean에 제공되는 유일한 인스턴스입니다.

그러나, 싱글톤 Bean이 런타임에 프로토 타입 Bean의 새로운 인스턴스를 반복적으로 획득하기를 원한다고 가정해봅시다. 주입은 스프링 컨테이너가 싱글톤 Bean을 인스턴스화하고 이것의 의존성을 처리하고 주입하려고 할 때 한번만 발생 히기 때문에, 프로토 타입 Bean의 종속성을 싱글톤 Bean에 삽입할 수 없습니다. 런타임에 두 번 이상 프로토 타입 Bean의 새로운 인스턴스가 필요할 경우 메소드 삽입을 참조하십시오.

1.5.4. Request, Session, Application, and WebSocket Scopes

요청, 세션, 응용 프로그램, 웹 소켓 영역은 XmlWebApplicationContext와 같은 웹 인식 스프링 ApplicationContext 구현을 사용하는 경우에만 사용할 수 있습니다. 이 범위들을 ClassPathXmlApplicationContext와 같은 일반 스프링 IoC 컨테이너와 함께 사용하면, 알 수 없는 Bean 범위라는 의미의 IllegalStateException이 발생합니다.

  • (+) 일반 스프링 어플리케이션이 아닌 스프링 MVC 웹 어플리케이션에서만 사용 가능

초기의 웹 구성

요청, 세션, 응용 프로그램 및 웹 소켓 레벨에서 Bean을 지원하려면, Bean을 정의하기 전에 약간의 초기 구성이 필요합니다. (이 초기 설정은 싱글톤과 프로토 타입의 표준 범위에서는 필요하지 않습니다)

이 초기 설정을 수행하는 방법은 특정 서블릿 환경에 따라 다릅니다.

스프링 웹 MVC에서 범위가 지정된 Bean에 접근하면, 사실상 스프링 DispatcherServlet에 의해 처리되는 요청 내에서 특별한 설정이 필요하지 않습니다. DispatcherServelet은 이미 모든 관련된 상태를 노출합니다.

스프링 DispatcherServlet 외부에서 요청이 처리되거나 서블릿 2.5 웹 컨테이너를 사용하는 경우에는(예: JSF 또는 Struts 사용 시), org.springframework.web.context.request.RequestContextListener, ServeletRequestListener를 등록 해야합니다. 서블릿 3.0 이상에서는 WebApplicationIntializer 인터페이스를 사용하여 프로그래밍 방식으로 수행할 수 있습니다. 또는 이전 컨테이너의 경우, 웹 응용 프로그램의 web.xml 파일에 다음 선언을 추가합니다.

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

또는 리스너 설정에 문제가 있는 경우, 스프링의 RequestContextFilter 사용을 고려하십시오. 필터 매핑은 주변 웹 응용 프로그램의 구성에 따라 다르므로 적절하게 변경해야 합니다. 다음 목록은 웹 응용프로그램의 필터 부분을 보여줍니다.

<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 요청 객체를 바인딩합니다. 이렇게 하면, 요청 및 세션 범위의 Bean을 호출 체인에서 사용할 수 있습니다.

Request scope

Bean 정의에 대한 다음의 XML 구성을 고려하십시오.

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

스프링 컨테이너는 각 HTTP 요청마다 loginAction Bean 정의를 사용하여 LoginAction Bean의 새로운 인스턴스를 생성합니다. 즉, loginAction Bean은 HTTP 요청 레벨에서 범위가 지정됩니다. 동일한 loginAction Bean 정의에서 작성된 다른 인스턴스는 상태 변경 사항을 볼 수 없기 때문에, 원하는 만큼 생성한 인스턴스의 내부 상태를 변경할 수 있습니다. 그것들은 개별 요청에서 특히 중요합니다. 요청이 처리를 완료하면, 요청에 스코프된 Bean은 버려집니다.

  • (+) 각 개의 웹 요청 별로 새로운 빈을 사용하려는 경우에 유용

어노테이션 기반 구성 요소 또는 자바 구성을 사용하는 경우, @RequestScope 어노테이션을 사용하여 요청 범위에 대한 컴포넌트를 할당할 수 있습니다. 다음 예제에서 이를 수행하는 방법을 보여줍니다.

@RequestScope
@Component
public class LoginAction {
    // ...
}

Session Scope

Bean 정의에 대한 다음의 XML 구성을 고려하십시오.

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

스프링 컨테이너는 단일 HTTP 세션의 라이프 타임동안 userPreferences Bean의 정의를 사용하여 UserPreference의 새로운 인스턴스를 생성합니다. 즉, userPreferences Bean은 HTTP 세션 레벨에서 효과적으로 범위가 지정됩니다. 요청 Bean과 마찬가지로, 동일한 usrePreferences Bean 정의에서 생성된 인스턴스를 사용하는 다른 HTTP 세션 인스턴스가 이러한 변경 사항을 상태로 보지 않는다는 것을 알면서 원하는만큼 생성된 인스턴스 내부 상태를 변경할 수 있습니다. 이것은 개별 HTTP 세션에 특화되어 있기 때문입니다. HTTP 세션이 결국 버려지면, 특정HTTP 세션으로 범위가 지정된 Bean도 버려집니다.

  • (+) 각 개의 HTTP 세션 별로 새로운 빈을 생성해서 하나의 클라이언트의 계속된 요청에 대응하려는 경우에 유용

어노테이션 기반 구성 요소 또는 자바 구성을 사용하는 경우, @SessionScope 어노테이션을 사용하여 세션 범위에 대한 구성 요소를 할당할 수 있습니다.

@SessionScope
@Component
public class UserPreferences {
    // ...
}

Application Scope

Bean 정의에 대한 다음의 XML 구성을 고려하십시오.

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

스프링 컨테이너는 전체 웹 어플리케이션에 대해 appPreferences Bean 정의를 한번 사용하여 AppPreferences Bean의 새로운 인스턴스를 생성합니다. 즉, appPreferences Bean은 ServletContext 레벨에서 범위가 지정되고, 일반 ServletContext 속성으로 저장됩니다. 이것은 스프링 싱글톤 Bean과 다소 비슷하지만, 두 가지 중요한 점에서 다릅니다 : 스프링 ApplicationContext가 아닌 ServletContext 당 하나의 싱글톤이며(특정 웹 어플리케이션에 여러 개가 있을 수 있음) 실제로 노출되어 있으므로 ServletContext 속성으로 표시됩니다.

  • (+) ApplicationContext Vs. ServletContext

    • ApplicationContext : 전체 계층 구조에서 최 상단에 위치한 컨텍스트. 서로 다른 서블릿 컨텍스트에서 공유해야하는 빈들을 등록해놓고 사용

    • ServletContext : 서블릿에서만 이용되는 컨텍스트.

어노테이션 기반 구성 요소 또는 자바 구성을 사용하는 경우, @ApplicationScope 어노테이션을 사용하여 응용 프로그램 범위에 구성 요소를 할당할 수 있습니다. 다음 에제에서는 이를 수행하는 방법을 보여줍니다.

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}

Scoped Beans as Dependencies

스프링 IoC 컨테이너는 객체(Bean)의 인스턴스화 뿐만 아니라 공동 작업자(또는 의존성)의 배선까지도 관리합니다. 예를 들어, HTTP 요청 범위가 지정된 Bean을 더 긴 수명 범위의 다른 Bean에 삽입하려는 경우, 범위가 지정된 Bean 대신 AOP 프록시를 삽입하도록 선택할 수 있습니다. 즉, 범위가 지정된 객체와 동일한 공용 인터페이스를 노출하는 프록시 객체를 삽입해야만 관련 범위(예: HTTP 요청)에서 실제 대상 객체를 검색하고 메소드 호출을 실제 객체에 위임할 수도 있습니다.

  • (+) 싱글톤 빈에서 요청 및 세션 범위의 빈을 참조할 경우에, 스프링에서는 scoped-proxy 개념으로 빈을 등록할 수 있도록 제공 해줌. (AOP를 통해 빈의 scope에 따라 바꿔치기해주는 방식)

    • 스프링은 자체적으로 프록시 기반의 AOP를 지원

      • 프록시 : 클라이언트가 사용하려는 실제 대상인 것처럼 위장하여 클라이언트의 요청을 받아주어 처리하는 대리자 역할. (보조 업무를 Proxy에게 맡기고, 필요 없으면 Proxy를 지워버리면 됨)

또한, 싱글톤으로 범위가 지정된 Bean 간에 <aop:scoped-proxy/>를 사용할 수도 있습니다. 그런 다음 참조는 직렬화할 수 있고 따라서 비 직렬화 시에 대상 싱글톤 Bean을 다시 얻을 수 있는 중간 프록시를 통과합니다.

스코프 프로토 타입의 Bean에 대해 <aop:scoped-proxy/>를 선언하면, 공유된 프록시의 모든 메소드 호출은 호출이 전달되는 새로운 목표 인스턴스를 생성하게 됩니다.

또한, 범위가 지정된 프록시만이 수명이 단축되는 짧은 범위의 Bean에 접근할 수 있는 유일한 방법이 아닙니다. 주입 지점 (즉, 생성자 또는 setter 인수 또는 autowired 필드)을 ObjectFacotry 으로 선언할 수 있습니다. getObject() 호출은 필요할 때마다 현재 인스턴스를 검색할 수 있습니다. 인스턴스를 작성하거나 별도로 저장하십시오.

확장된 변형으로, getAvailable 및 getIfUnique를 포함하여 몇 가지 추가 엑세스 변형을 제공하는 ObjectProvider 을 선언할 수 있습니다.

이 JSR-330 변형은 Provider라고 하며 Provider 선언 및 모든 검색 시도에 해당하는 get() 호출과 함께 사용됩니다. 전체 JSR-330에 대한 자세한 내용은 여기를 참조하십시오.

다음 예제의 구성은 한 줄에 불과하지만, 이유와 방법을 이해하는 것이 중요합니다.

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

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/> 
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

The line that defines the proxy.

이러한 프록시를 만들려면, 범위가 지정된 Bean의 정의에 자식 <aop:scoped-proxy /> 요소를 삽입합니다. (만들기 위해 프록시 유형 선택 및 XML 스키마 기반 구성 참조) 요청, 세션 및 사용자 정의 범위 수준에서 범위가 정의된 Bean의 정의가 요소를 필요로 하는 이유는 무엇입니까? 다음과 같은 싱글톤 Bean의 정의를 생각해보시고, 위에서 언급한 스코프에 정의할 필요가 있는 것과 대조하십시오. (다음과 같은 userPreferences bean 정의는 완전하지 않습니다)

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

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

앞의 예제에서, 싱글톤 Bean(userManager)에는 HTTP 세션 범위의 Bean(userPreferences)에 대한 참조가 삽입됩니다. 여기서 주목할 점은 userManager Bean이 싱글톤이라는 점입니다. 컨테이너 당 정확히 한번만 인스턴스화되고 종속성(이 경우에는 userPreferences Bean 하나)도 한 번만 주입됩니다. 즉, userManager Bean은 완전히 동일한 userPreferences 객체(즉, 원래 주입된 객체)에서만 작동합니다.

이것은 수명이 짧은 범위가 지정된 Bean을 수명이 긴 범위가 지정된 Bean으로 주입할 때 원하는 동작이 아니다. (예 : HTTP 세션 범위의 협업 Bean을 싱글톤 Bean에 대한 종속성으로 주입할 때) 오히려, 하나의 userManager 객체가 필요하며, HTTP 세션의 수명 동안, HTTP 세션에 특정한 userPreferences 객체가 필요합니다. 따라서, 컨테이너는 범위 지정 메커니즘(HTTP 요청, 세션 등)에서 실제 UserPreferences 클래스 (이상적으로는 UserPreferences 인스턴스인 객체)와 완전히 동일한 공용 인터페이스를 제공하는 객체를 만듭니다. 컨테이너는 이 userPreferences 참조가 프록시임을 모르는 userManager Bean에 이 프록시 객체를 주입합니다. 이 예제에서 UserManager 인스턴스가 종속성이 삽입된 UserPreferences 객체에서 메소드를 호출하면, 실제로는 프록시에서 메소드를 호출합니다. 그런 다음 프록시는 실제 UserPreferences 객체에서 HTTP 세션을 가져 오고, 검색된 실제 UserPreferences 객체에 메소드 호출을 위임합니다.

  • (+) 문제 해결을 위해, 최초 userManager 빈 생성 시에 userPreferences 빈실제 인스턴스를 생성해서 주는 것이 아니라, Proxy 객체를 생성해서 주는 방법이 있음. -> 스프링에서 <aop:scoped-proxy/> 태그를 지원함.

따라서, 요청 및 세션 범위의 Bean을 협업 객체에 주입할 때 다음 예제와 같은 구성이 필요합니다.

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

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
  • (+) userManager가 싱글톤 빈이고, userPreferences가 세션 빈 이어도, userPreferences에 대한 실제 인스턴스가 아닌 Proxy 객체를 참조하고 있으므로 세션마다 생성되는 userPreferences 빈의 인스턴스 참조를 제대로 주입할 수 있게 됨.

Choosing the Type of Proxy to Create

기본적으로, 스프링 컨테이너가 <aop:scoped-proxy/> 요소로 마크업된 Bean에 대한 프록시를 생성하면, CGLIB 기반 클래스 프록시가 생성됩니다.

CGLIB 프록시는 공개 메소드 호출만을 차단합니다! 이러한 프록시에서 비공개 메소드를 호출하지 마십시오. 이들은 실제 범위가 지정된 대상 객체에 위임되지 않습니다.

대안적으로, 요소의 proxy-target-class 속성 값에 false를 지정하여 범위가 지정된 Bean에 대한 표준 JDK 인터페이스 기반 프록시를 만들도록 스프링 컨테이너를 구성할 수 있습니다. JDK 인터페이스 기반 프록시를 사용하면, 응용 프로그램 클래스 경로에 추가 라이브러리가 필요하지 않으므로, 이러한 프록싱에 영향을 미칠 수 있습니다. 그러나, 이는 범위가 지정된 Bean의 클래스가 최소한 하나의 인터페이스를 구현해야 하며 범위가 지정된 Bean이 삽입되는 모든 공동 작업자가 해당 인터페이스 중 하나를 통해 Bean을 참조해야 함을 의미합니다. 다음 예제에서는 인터페이스를 기반으로 하는 프록시를 보여줍니다.

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

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

클래스 기반 또는 인터페이스 기반 프록시 선택에 대한 자세한 내용은 프록시 메커니즘을 참조하십시오.

  • (+) 프록시 기반 AOP 기술 메커니즘 2가지 -> 인터페이스 유무 차이

    • CGLIB : AOP 타깃이 되는 클래스가 인터페이스를 구현하지 않았을 경우 사용

    • JDK Dynamic Proxy : AOP 타깃이 되는 클래스가 인터페이스를 구현했을 경우 사용.

      • 스프링에서는 프록시 대상의 객체가 최소 하나의 인터페이스를 구현했다면, JDK 동적 프록시를 사용하는 반면, 대상 객체의 인터페이스를 구현하지 않았다면, Cglib를 사용해 프록시 객체를 생성함. (원래 Cglib는 스프링에 존재하지 않았는데, 3.2부터 재패키징되어 포함됨)

1.5.5. Custom Scopes

빈 범위 지정 메커니즘은 확장 가능합니다. 사용자 범위를 정의하거나 기존 범위를 다시 정의할 수도 있지만, 후자는 좋지 않은 행위로 간주되며 기본 내장된 singletonprototype범위를 재정의 할 수는 없습니다.

[사용자 지정 범위 생성]

사용자 지정 범위를 스프링 컨테이너에 통합하려면, 이 섹션에서 설명하는 org.springframework.beans.factory.config.Scope인터페이스를 구현해야 합니다. 사용자 지정 범위를 구현하는 방법에 대한 아이디어는 스프링 프레임 워크 자체와 Scope javadoc와 함께 제공되는 Scope구현을 참조하십시오. 이 구현체는 구현해야 하는 메소드에 대해 자세히 설명합니다.

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

다음 메서드는 의존 하는 범위에서 객체를 반환합니다. 예를 들어, 세션 범위 구현은 세션 범위의 빈을 반환합니다. (존재하지 않는 경우에는, 나중에 참조할 수 있도록 빈을 바인딩 한 후 새로운 인스턴스를 반환합니다)

Object get(String name, ObjectFactory objectFactory)

예를 들어, 세션 범위 구현은 기본 세션에서 세션 범위 빈을 제거합니다. 객체가 반환되지만, 지정된 이름을 가진 객체가 발견되지 않는 경우에는 NULL을 돌려줄 수가 있습니다. 다음 메서드는 기본 범위에서 객체를 제거합니다:

Object remove(String name)

다음 메소드는 범위가 소멸되거나 범위의 지정된 객체가 소멸될 때 범위가 실행해야 하는 콜백을 등록합니다:

void registerDestructionCallback(String name, Runnable destructionCallback)

소멸 콜백에 대한 자세한 내용은 javadoc 또는 스프링 범위 구현체를 참조하십시오.

다음 메소드는 기본 범위의 대화 식별 자를 가져옵니다:

String getConversationId()

이 식별자는 각 범위마다 다릅니다. 세션 범위 구현의 경우, 이 식별자는 세션 식별자가 될 수 있습니다.

[사용자 지정 범위 사용]

하나 이상의 사용자 정의 범위 구현을 작성하고 테스트한 후, 스프링 컨테이너가 새 범위를 인식하도록 해야 합니다. 다음 메소드는 스프링 컨테이너에 새로운 Scope를 등록하는 핵심 메소드입니다:

void registerScope(String scopeName, Scope scope);

이 메소드는 스프링과 함께 제공되는 대부분의 구체적인 ApplicationContext구현에서 BeanFactory속성을 통해 사용할 수 있는 ConfigurableBeanFactory인터페이스에서 선언됩니다.

registerScope(..) 메서드의 첫 번째 인수는 범위와 연관된 고유한 이름입니다. 스프링 컨테이너 자체에서 그러한 이름의 예는 singletonprototype입니다. registerScope(..)메소드의 두 번째 인수는 등록하고 사용하려는 사용자 정의 범위 구현의 실제 인스턴스입니다.

사용자 정의 범위 구현을 작성한 후 다음 예제와 같이 등록한다고 가정합니다.

다음 예제는 스프링에 포함된 SimpleThreadScope를 사용하지만 기본적으로 등록되지는 않습니다. 이 설명은 사용자 지정 범위 구현체에서도 동일하게 적용될 것입니다.

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

그런 다음, 다음과 같이 사용자 정의 범위의 범위 지정 규칙을 준수하는 빈 정의를 작성할 수 있습니다:

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

사용자 지정 범위 구현체를 사용할 때 범위를 프로그래밍 적으로 등록하는 데 제한은 없습니다. 또한, 다음 예제와 같이 CustomScopeConfigurer클래스를 사용하여 선언적으로 범위를 등록할 수도 있습니다:

<?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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.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="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>

FactoryBean 구현체에 <aop:scoped-proxy/>가 있으면, 이는 getObject()에서 반환된 객체가 아닌 스스로 범위를 가진 팩토리 빈입니다.

Last updated