Bean Scopes(한글)

Bean 정의를 생성하면 해당 Bean 정의에 의해 정의된 클래스의 실제 인스턴스를 생성하는 레시피를 생성하게 됩니다. Bean 정의가 레시피라는 개념이 중요한 이유는 클래스와 마찬가지로 하나의 레시피에서 많은 객체 인스턴스를 생성할 수 있기 때문입니다.

특정 Bean 정의에서 생성되는 객체에 연결될 다양한 종속성과 구성 값을 제어할 수 있을 뿐만 아니라 특정 Bean 정의에서 생성되는 객체의 Scope도 제어할 수 있습니다. 이 접근 방식은 Java 클래스 수준에서 객체의 Scope를 베이크(bake)할 필요 없이 구성을 통해 생성하는 객체의 Scope를 선택할 수 있기 때문에 강력하고 유연합니다. Bean은 여러 Scope 중 하나에 배포되도록 정의할 수 있습니다. Spring 프레임워크는 6개의 스코프를 지원하며, 이 중 4개의 스코프는 web-aware ApplicationContext 를 사용하는 경우에만 사용할 수 있습니다. 사용자 정의 Scope를 생성할 수도 있습니다.

다음 표는 지원되는 Scope에 대해 설명합니다:

Table 1. Bean scopes
Scope Description

singleton

(기본값) 각 Spring IoC 컨테이너에 대해 단일 Bean 정의의 Scope를 단일 객체 인스턴스로 지정합니다.

prototype

단일 Bean 정의의 Scope를 원하는 수의 객체 인스턴스로 지정합니다.

request

단일 Bean 정의의 Scope를 단일 HTTP 요청의 라이프사이클로 한정합니다. 즉, 각 HTTP 요청에는 단일 Bean 정의에 기반하여 생성된 Bean의 자체 인스턴스가 있습니다. web-aware Spring ApplicationContext 의 컨텍스트에서만 유효합니다.

session

단일 Bean 정의를 HTTP Session 의 라이프사이클로 Scope를 지정합니다. web-aware Spring ApplicationContext 의 컨텍스트에서만 유효합니다.

application

단일 Bean 정의를 ServletContext 의 라이프사이클로 Scope를 지정합니다. web-aware Spring ApplicationContext 의 컨텍스트에서만 유효합니다.

websocket

단일 Bean 정의를 WebSocket 의 라이프사이클로 Scope를 지정합니다. web-aware 스프링 ApplicationContext 의 컨텍스트에서만 유효합니다.

스레드(thread) Scope를 사용할 수 있지만 기본적으로 등록되어 있지 않습니다. 자세한 내용은 SimpleThreadScope 설명서를 참조하세요. 이 스코프 또는 다른 사용자 정의 스코프를 등록하는 방법에 대한 지침은 사용자 정의 스코프 사용을 참조하세요.

싱글톤 Scope(The Singleton Scope)

싱글톤 Bean의 공유 인스턴스는 하나만 관리되며, 해당 Bean 정의와 일치하는 ID를 가진 Bean에 대한 모든 요청은 Spring 컨테이너에서 해당 특정 Bean 인스턴스 하나를 반환하게 됩니다.

다시 말해, Bean 정의를 정의하고 이것이 싱글톤으로 Scope가 지정되면 Spring IoC 컨테이너는 해당 Bean 정의에 의해 정의된 객체의 인스턴스를 정확히 하나만 생성합니다. 이 단일 인스턴스는 이러한 싱글톤 Bean의 캐시에 저장되며, 이후 해당 이름의 Bean에 대한 모든 요청과 참조는 캐시된 객체를 반환합니다. 다음 이미지는 싱글톤 Scope의 작동 방식을 보여줍니다:

singleton

Spring의 싱글톤 Bean 개념은 GoF(Gang of Four) 패턴 책에 정의된 싱글톤 패턴과 다릅니다. GoF 싱글톤은 특정 클래스의 인스턴스가 ClassLoader당 하나만 생성되도록 객체의 Scope를 하드코딩합니다. Spring 싱글톤의 Scope는 컨테이너별 및 Bean별로 설명하는 것이 가장 좋습니다. 즉, 단일 Spring 컨테이너에서 특정 클래스에 대해 하나의 Bean을 정의하면 Spring 컨테이너는 해당 Bean 정의에 의해 정의된 클래스의 인스턴스를 하나만 생성합니다. 싱글톤 Scope는 Spring의 기본 Scope입니다. 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"/>

프로토타입 Scope(The Prototype Scope)

비싱글톤 프로토타입 Scope의 Bean 배포는 특정 Bean에 대한 요청이 있을 때마다 새로운 Bean 인스턴스를 생성합니다. 즉, Bean이 다른 Bean에 주입되거나 컨테이너에서 getBean() 메서드 호출을 통해 요청됩니다. 원칙적으로 모든 상태 저장 Bean에는 프로토타입 스코프를, 상태 비저장 Bean에는 싱글톤 스코프를 사용해야 합니다.

다음 다이어그램은 Spring 프로토타입의 Scope를 보여줍니다:

prototype

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

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

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

다른 Scope와 달리 Spring은 프로토타입 Bean의 전체 라이프사이클을 관리하지 않습니다. 컨테이너는 프로토타입 객체를 인스턴스화, 구성 및 기타 방식으로 어셈블하여 클라이언트에 전달하며, 해당 프로토타입 인스턴스에 대한 추가 기록은 남기지 않습니다. 따라서 초기화 수명 주기 콜백 메서드는 Scope에 관계없이 모든 객체에서 호출되지만, 프로토타입의 경우 구성된 소멸 수명 주기 콜백은 호출되지 않습니다. 클라이언트 코드는 프로토타입 Scope의 객체를 정리하고 프로토타입 Bean이 보유하고 있는 값비싼 리소스를 해제해야 합니다. Spring 컨테이너가 프로토타입 Scope의 Bean이 보유한 리소스를 해제하도록 하려면, 정리해야 하는 Bean에 대한 참조가 있는 사용자 정의 Bean post-processor를 사용해보십시오.

어떤 측면에서, 프로토타입 Scope의 Bean에 대한 Spring 컨테이너의 역할은 Java new 연산자를 대체하는 것입니다. 그 이후의 모든 라이프사이클 관리는 클라이언트에서 처리해야 합니다. (Spring 컨테이너에서 Bean의 라이프사이클에 대한 자세한 내용은 라이프사이클 콜백을 참조하십시오).

프로토타입-빈 종속성이 있는 싱글톤 Bean(Singleton Beans with Prototype-bean Dependencies)

프로토타입 Bean에 대한 종속성이 있는 싱글톤 Scope Bean을 사용하는 경우, 종속성은 인스턴스화 시점에 확인된다는 점에 유의하세요. 따라서 프로토타입 Scope Bean을 싱글톤 Scope Bean에 종속성을 주입하는 경우 새 프로토타입 Bean이 인스턴스화된 다음 싱글톤 Bean에 종속성이 주입됩니다. 프로토타입 인스턴스는 싱글톤 Scope Bean에 제공되는 유일한 인스턴스입니다.

그러나 런타임에 싱글톤 Scope Bean이 프로토타입 Scope Bean의 새 인스턴스를 반복적으로 획득하기를 원한다고 가정해 보겠습니다. Spring 컨테이너가 싱글톤 Bean을 인스턴스화하고 해당 종속성을 해결하고 주입할 때 한 번만 발생하기 때문에 프로토타입 Scope Bean을 싱글톤 Bean에 종속성을 주입할 수 없습니다. 런타임에 프로토타입 Bean의 새 인스턴스가 두 번 이상 필요한 경우 메서드 주입을 참조하세요.

리퀘스트, 세션, 애플리케이션, 웹소켓 Scope(Request, Session, Application, and WebSocket Scopes)

request, session, application, websocket Scope는 web-aware Spring ApplicationContext 구현(예: XmlWebApplicationContext)을 사용하는 경우에만 사용할 수 있습니다. 이러한 Scope를 ClassPathXmlApplicationContext 와 같은 일반 Spring IoC 컨테이너와 함께 사용하는 경우, 알 수 없는 Bean Scope에 대해 발생하는 IllegalStateException 이 throw됩니다.

초기 웹 구성(Initial Web Configuration)

request, session, application, websocket 수준(웹 Scope Bean) 의 Scope를 적용하려면, Bean을 정의하기 전에 약간의 초기 구성이 필요합니다. (표준 Scope인 singleton 및 `prototype`의 경우 이 초기 설정이 필요하지 않습니다.)

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

Spring Web MVC 내에서 Scope가 지정된 Bean에 액세스하는 경우, 즉 사실상 Spring DispatcherServlet 에 의해 처리되는 요청 내에서 액세스하는 경우 특별한 설정이 필요하지 않습니다. DispatcherServlet 은 이미 모든 관련 상태를 제공합니다.

서블릿 웹 컨테이너를 사용하는 경우, Spring의 DispatcherServlet 외부에서 요청을 처리하는 경우(예: JSF 사용 시), org.springframework.web.context.request.RequestContextListener ServletRequestListener 를 등록해야 합니다. 이 작업은 WebApplicationInitializer 인터페이스를 사용하여 프로그래밍 방식으로 수행할 수 있습니다. 또는 웹 애플리케이션의 web.xml 파일에 다음 선언을 추가할 수도 있습니다:

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

또는 리스너 설정에 문제가 있는 경우 Spring의 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 요청 객체를 해당 요청을 서비스하는 Thread 에 바인딩하는 것입니다. 이렇게 하면 request 및 session Scope의 Bean을 콜 체인의 더 아래에서 사용할 수 있습니다.

리퀘스트 Scope(Request Scope)

Bean 정의에 대한 다음 XML 구성을 고려하세요:

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

Spring 컨테이너는 모든 HTTP Request에 대해 loginAction Bean 정의를 사용하여 loginAction Bean의 새 인스턴스를 생성합니다. 즉, loginAction Bean은 HTTP request 수준에서 Scope가 지정됩니다. 생성된 인스턴스의 내부 상태는 원하는 만큼 변경할 수 있는데, 동일한 loginAction Bean 정의에서 생성된 다른 인스턴스에는 이러한 상태 변경이 표시되지 않기 때문입니다. 이는 개별 요청에만 해당됩니다. 요청이 처리를 완료하면 해당 Request으로 Scope가 지정된 Bean은 삭제됩니다.

어노테이션 기반 컴포넌트 또는 Java 구성을 사용하는 경우 @RequestScope 어노테이션을 사용하여 컴포넌트를 request Scope에 할당할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:

  • Java

  • Kotlin

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

세션 Scope(Session Scope)

Bean 정의에 대한 다음 XML 구성을 고려하세요:

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

Spring 컨테이너는 단일 HTTP Session 의 수명 기간 동안 userPreferences Bean 정의를 사용하여 UserPreferences Bean의 새 인스턴스를 생성합니다. 다시 말해, userPreferences Bean은 HTTP Session 수준에서 효과적으로 Scope가 지정됩니다. Request Scope가 지정된 Bean과 마찬가지로, 생성된 인스턴스의 내부 상태를 원하는 만큼 변경할 수 있으며, 동일한 userPreferences Bean 정의에서 생성된 인스턴스를 사용하는 다른 HTTP Session 인스턴스에서는 이러한 상태 변경이 개별 HTTP Session 에만 해당되기 때문에 볼 수 없다는 것을 알 수 있습니다. HTTP Session 이 결국 폐기되면, 해당 특정 HTTP Session 으로 Scope가 지정된 Bean도 폐기됩니다.

어노테이션 기반 컴포넌트 또는 Java 구성을 사용하는 경우, @SessionScope 어노테이션을 사용하여 session Scope에 컴포넌트를 할당할 수 있습니다.

  • Java

  • Kotlin

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

애플리케이션 Scope(Application Scope)

Bean 정의에 대한 다음 XML 구성을 고려하세요:

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

Spring 컨테이너는 전체 웹 애플리케이션에 대해 appPreferences Bean 정의를 한 번 사용하여 AppPreferences Bean의 새 인스턴스를 생성합니다. 즉, appPreferences Bean은 ServletContext 수준에서 Scope가 지정되고 일반 ServletContext 어트리뷰트로 저장됩니다. 이것은 Spring 싱글톤 Bean과 다소 유사하지만 두 가지 중요한 점에서 다릅니다: Spring ApplicationContext(특정 웹 애플리케이션에 여러 개가 있을 수 있음)가 아닌 ServletContext 별 싱글톤이며, 실제로 노출되어 ServletContext 어트리뷰트로 표시된다는 점입니다.

어노테이션 기반 컴포넌트 또는 Java 구성을 사용하는 경우, @ApplicationScope 어노테이션을 사용하여 application Scope에 컴포넌트를 할당할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:

  • Java

  • Kotlin

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

웹소켓 Scope(WebSocket Scope)

웹소켓 Scope는 웹소켓 세션의 수명 주기와 연관되어 있으며 웹소켓 애플리케이션을 통한 (역자설명 : Simple Text Oriented Messaging Protocol)STOMP에 적용됩니다. 자세한 내용은 WebSocket Scope를 참조하세요.

Scope가 지정된 Bean을 종속성으로 사용하기(Scoped Beans as Dependencies)

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

또한 singleton 으로 Scope가 지정된 Bean 간에 <aop:scoped-proxy/> 를 사용할 수 있으며, 참조는 직렬화 가능하므로 역직렬화 시 대상 싱글톤 Bean을 다시 얻을 수 있는 중간 프록시를 거치게 됩니다.

prototype Bean에 대해 <aop:scoped-proxy/> 를 선언하면 (역자설명 : 호출을 전달하는 프록시 객체가 생성됨을 설명하는 듯…​)공유 프록시의 모든 메서드 호출은 호출이 전달되는 새 대상 인스턴스를 생성하게 됩니다.

또한, Scope가 지정된 프록시는 라이프사이클에 안전한 방식으로 더 짧은 Scope의 Bean에 액세스할 수 있는 유일한 방법은 아닙니다. 또한 주입 지점(즉, 생성자 또는 설정자 인수 또는 Autowired 필드)을 ObjectFactory<MyTargetBean> 으로 선언하여 인스턴스를 보유하거나 별도로 저장하지 않고 필요할 때마다 getObject() 호출을 통해 현재 인스턴스를 검색할 수 있도록 할 수 있습니다.

확장된 변형으로 ObjectProvider<MyTargetBean> 을 선언하여 getIfAvailablegetIfUnique 를 비롯한 여러 추가 액세스 변형을 제공할 수 있습니다.

이에 대한 JSR-330 변형은 Provider 라고 불리며 모든 검색 시도에 대해 Provider<MyTargetBean> 선언 및 해당 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
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
		https://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/> (1)
	</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>
1 프록시를 정의하는 줄입니다.

이러한 프록시를 생성하려면 Scope가 지정된 Bean 정의에 하위 <aop:scoped-proxy/> 요소를 삽입합니다(생성할 프록시 유형 선택XML 스키마 기반 구성 참조).

request, session 및 사용자 정의 Scope 수준에서 Scope가 지정된 Bean의 정의에 일반적인 시나리오에서 <aop:scoped-proxy/> 요소가 필요한 이유는 무엇일까요? 다음 싱글톤 Bean 정의를 고려하고 앞서 언급한 Scope에 대해 정의해야 하는 것과 대조해 보세요(아래의 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 Session Scope의 Bean(userPreferences)에 대한 참조가 주입되었습니다. 여기서 중요한 점은 userManager Bean이 싱글톤이라는 점입니다. 컨테이너당 정확히 한 번만 인스턴스화되며, 그 종속성(이 경우 userPreferences Bean 하나만)도 한 번만 주입된다는 것입니다. 즉, userManager Bean은 정확히 동일한 userPreferences 객체(즉, 원래 주입된 객체)에서만 작동합니다.

이는 수명이 짧은 Scope의 Bean을 수명이 긴 Scope의 Bean에 주입할 때 원하는 동작이 아닙니다(예: 싱글톤 Bean에 종속성으로 HTTP Session Scope의 협업자 Bean을 주입하는 경우). 그 대신, 단일 userManager 객체가 필요하며, HTTP Session 의 수명 동안에는 HTTP Session`에 특정한 `userPreferences 객체가 필요합니다. 따라서 컨테이너는 Scope 지정 메커니즘(HTTP 요청, Session 등)에서 실제 UserPreferences 객체를 가져올 수 있는 UserPreferences 클래스와 정확히 동일한 공용 인터페이스를 노출하는 객체(이상적으로는 UserPreferences 인스턴스인 객체)를 생성합니다. 컨테이너는 이 프록시 객체를 이 UserPreferences 참조가 프록시라는 것을 인식하지 못하는 userManager Bean에 주입합니다. 이 예제에서 UserManager 인스턴스가 종속성이 주입된 UserPreferences 객체에서 메서드를 호출하면 실제로는 프록시에서 메서드를 호출하는 것입니다. 그런 다음 프록시는 (이 경우) HTTP Session 에서 실제 UserPreferences 객체를 가져오고 메서드 호출을 검색된 실제 UserPreferences 객체에 위임합니다.

따라서 다음 예제에서 볼 수 있듯이 requestsession Scope 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>

생성할 프록시 유형 선택(Choosing the Type of Proxy to Create)

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

CGLIB 프록시는 private 메서드를 가로채지 않습니다. 이러한 프록시에서 private 메서드를 호출하려고 시도하면 실제 Scope가 지정된 대상 객체에 위임되지 않습니다.

또는, <aop:scoped-proxy/> 요소의 proxy-target-class 속성 값에 `false`를 지정하여 이러한 Scope Bean에 대한 표준 JDK 인터페이스 기반 프록시를 생성하도록 Spring 컨테이너를 구성할 수 있습니다. JDK 인터페이스 기반 프록시를 사용한다는 것은 애플리케이션 클래스 경로에 이러한 프록시에 영향을 주는 추가 라이브러리가 필요하지 않다는 것을 의미합니다. 그러나 이는 또한 Scope가 지정된 Bean의 클래스가 하나 이상의 인터페이스를 구현해야 하며 Scope가 지정된 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>

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

Request/Session 참조 직접 주입하기

팩토리 Scope의 대안으로, Spring WebApplicationContext 는 간단한 유형의 Autowiring을 통해 HttpServletRequest, HttpServletResponse, HttpSession, WebRequest 및 (JSF가 있는 경우) FacesContext 및 `ExternalContext`를 Spring 관리 Bean에 주입하는 것도 지원합니다. Spring은 일반적으로 이러한 요청 및 세션 객체에 대한 프록시를 주입하는데, 이는 팩토리 Scope의 Bean에 대한 Scope 프록시와 유사하게 싱글톤 Bean과 직렬화 가능한 Bean에서도 작동한다는 이점이 있습니다.

사용자 지정 Scope(Custom Scopes)

Bean Scope 메커니즘은 확장 가능합니다. 자체 Scope를 정의하거나 기존 Scope를 재정의할 수도 있지만, 후자는 나쁜 관행으로 간주되며 기본 제공 singletonprototype Scope를 재정의할 수 없습니다.

사용자 지정 Scope 만들기(Creating a Custom Scope)

사용자 정의 Scope를 Spring 컨테이너에 통합하려면 이 섹션에서 설명하는 org.springframework.beans.factory.config.Scope 인터페이스를 구현해야 합니다. 자체 스코프를 구현하는 방법에 대한 아이디어는 Spring 프레임워크 자체와 함께 제공되는 Scope 구현과 구현하는 데 필요한 방법을 자세히 설명하는 Scope javadoc을 참조하세요.

Scope 인터페이스에는 객체를 가져오고, 객체를 제거하고, 소멸시키는 네 가지 메서드가 있습니다.

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

  • Java

  • Kotlin

Object get(String name, ObjectFactory<?> objectFactory)
fun get(name: String, objectFactory: ObjectFactory<*>): Any

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

  • Java

  • Kotlin

Object remove(String name)
fun remove(name: String): Any

다음 메서드는 Scope가 파괴될 때 호출해야 하는 콜백을 등록합니다. 파괴되거나 Scope의 지정된 객체가 파괴될 때 호출해야 하는 콜백을 등록합니다:

  • Java

  • Kotlin

void registerDestructionCallback(String name, Runnable destructionCallback)
fun registerDestructionCallback(name: String, destructionCallback: Runnable)

소멸 콜백에 대한 자세한 내용은 javadoc 또는 Spring Scope 구현을 참조하세요.

다음 메서드는 기본 Scope에 대한 conversation identifier(식별자)를 가져옵니다:

  • Java

  • Kotlin

String getConversationId()
fun getConversationId(): String

이 식별자는 각 Scope마다 다릅니다. Session Scope 구현의 경우 이 식별자는 Session 식별자가 될 수 있습니다.

사용자 지정 Scope 사용(Using a Custom Scope)

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

  • Java

  • Kotlin

void registerScope(String scopeName, Scope scope);
fun registerScope(scopeName: String, scope: Scope)

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

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

다음 예시와 같이 사용자 정의 Scope 구현을 작성한 다음 등록한다고 가정해 보겠습니다.

다음 예제에서는 Spring에 포함되어 있지만 기본적으로 등록되지 않은 SimpleThreadScope`를 사용합니다. 사용자 정의 `Scope 구현에 대한 지침은 동일합니다.
  • Java

  • Kotlin

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

그런 다음에 다음과 같이 사용자 정의 Scope 의 Scope 지정 규칙을 준수하는 Bean 정의를 만들 수 있습니다:

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

사용자 정의 Scope 구현을 사용하면 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
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop
		https://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 구현을 위한 <bean> 선언 내에 <aop:scoped-proxy/> 를 배치하면 getObject() 에서 반환된 객체가 아니라 Factory Bean 자체의 Scope가 지정됩니다.