[Spring] 빈 스코프(BeanScope)
빈 스코프란?
스코프=범위
빈 스코프란 빈이 관리되는 범위를 의미합니다. (스코프=범위)
스프링 빈은 기본적으로 싱글톤으로 생성하여 관리됩니다. 싱글톤으로 생성된다는 것은 스프링 컨테이너의 시작과 함께 생성되고 종료될때까지 유지된다는 의미입니다. 스프링이 지원하는 스코프 전략은 여러가지가 있는데요. 스프링에서의 기본 설정이 싱글톤으로 되어있기 때문입니다.
기본값인 싱글톤 방식의 특징과 그 이외에 어떤 스코프 전략의 특징들을 간단히 살펴보겠습니다.
스프링이 지원하는 스코프 종류
- Singleton Scope
스프링에서의 기본 스코프 전략으로, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프입니다.
앱 구동 시에 ApplicationContext에 싱글톤으로 생성됩니다. - Prototype Scope
매번 사용될 때마다 Bean을 생성하는 전략입니다.
스프링 컨테이너는 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프입니다. - Web Scope
웹 환경에서만 지원하는 스코프 종류들로 Request, Session, Application, Socket Scope가 있다.
싱글톤 스코프
- 스프링에서의 기본 설정
- 애플리케이션에서 단 하나의 인스턴스로만 관리
- 단일 인스턴스는 싱글톤 Bean Cache에 저장
- 해당 Bean에 대한 모든 요청 및 참조는 캐시된 객체를 반환받아 실행
= 항상 같은 인스턴스의 Bean을 반환 - Stateless Bean으로 사용
아래와 같이 간단한 테스트를 해볼 수 있습니다. 싱글톤같은 경우 따로 스코프를 지정해주지 않아도 자동으로 싱글톤으로 생성되나 테스트 편의를 위해 어노테이션을 통해 스코프를 지정해 주었습니다.
public class SingletonTest {
@Test
void singletonBeanFind() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class);
SingletonBean bean1 = ac.getBean(SingletonBean.class);
SingletonBean bean2 = ac.getBean(SingletonBean.class);
System.out.println("bean1 = " + bean1);
System.out.println("bean2 = " + bean2);
assertThat(bean1).isSameAs(bean2);
ac.close();
}
@Scope("singleton")
static class SingletonBean {
@PostConstruct
public void init() {
System.out.println("SingletonBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("SingletonBean.destroy");
}
}
}
테스트 결과를 확인해 보면 동일한 객체를 각각 호출했으나 동일한 인스턴스임을 확인할 수 있습니다.
프로토타입 스코프
- 특정 Bean에 대한 요청마다 새로운 Bean 인스턴스 생성
- Stateful Bean에는 프로토타입 스코프 사용
- 스프링 컨테이너는 프로토타입 빈을 생성, 의존관계 주입, 초기화까지만 처리하고 그 이후는 관리하지 않음
=> 빈 생명주기 중 소멸 콜백이 호출되지 않음 - 프로토타입 빈을 관리할 책임은 빈을 받은 클라이언트에 있음
아래와 같이 간단한 테스트를 통해 확인해볼 수 있습니다. 기본값은 싱글톤이기 때문에 객체에 직접 스코프를 지정해줘야 프로토타입으로 사용할 수 있습니다. 객체 위에 @Scope("prototype") 을 작성하여 프로토타입 스코프로 사용할 것임을 작성해줍니다.
public class PrototypeTest {
@Test
void prototypeBeanFind() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
PrototypeBean bean1 = ac.getBean(PrototypeBean.class);
PrototypeBean bean2 = ac.getBean(PrototypeBean.class);
System.out.println("bean1 = " + bean1);
System.out.println("bean2 = " + bean2);
assertThat(bean1).isNotSameAs(bean2);
ac.close();
}
@Scope("prototype")
static class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
}
테스트 결과를 보면 각각 호출한 두개의 프로토타입 빈이 서로 다른 인스턴스임을 확인해볼 수 있습니다.
프로토타입 스코프를 싱글톤 빈과 함께 사용시 문제점
Singleton Bean 내부에 가지고 있는 Prototype Bean은 이미 주입 시 생성되었던 Bean이 됩니다. 주입 시점에 스프링 컨테이너에 요청하여 빈이 새로 생성된 것이지, 싱글톤 빈 내부에서 사용할 때마다 새로 생성되는 빈이 아니게 됩니다. 싱글톤 빈이 생성시점에만 의존관계를 주입 받기 때문에, 프로토타입 빈이 새로 생성되면 싱글톤 빈과 함께 계속 유지되게 되는 것입니다.
웹 스코프
싱글톤 스코프와 프로토타입 스코프를 제외하고는 모두 웹 환경에서만 적용되는 웹 스코프입니다. 웹 스코프에 해당되는 전략들은 프로토타입과 다르게 컨텍스트가 전체 생명주기를 관리해주며 소멸 콜백이 요청됩니다.
Request Scope
HTTP 요청마다 생성하는 전략입니다. 웹 요청이 들어오고 나갈때까지 유지되는 스코프로 하나의 HTTP 생명주기 마다 유지됩니다.
@RequestScope
@Component
public class LoginAction {
// ...
}
Session Scope
Web Session이 생성되고 종료될 때까지 유지되는 스코프입니다.
HTTP Session의 생명주기 안에 단 하나의 객체만 존재합니다. 이는 SpringMVC Web Application에서만 사용됩니다.
@SessionScope
@Component
public class UserPreferences {
// ...
}
Application Scope
Servlet Context 생명주기 동안 한 개의 Bean만 사용됩니다.
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
WebSocket Scope
웹소켓과 동일한 생명주기를 갖습니다.