빈 스코프란?
스프링 빈은 기본적으로 싱글톤으로 관리된다. 그래서 스프링 컨테이너는 스프링 싱글톤 컨테이너라고 불리기도 한다. 그러나 이것은 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문이다. "스코프"는 빈이 존재할 수 있는 범위를 뜻한다.
한마디로 빈 스코프는 빈이 존재할 수 있는 범위 또는 빈의 라이프사이클의 범위라고 생각할 수 있을 것 같다.
스프링은 아래와 같이 다양한 빈 스코프를 지원한다.
- 싱글톤 : default, 스프링 컨테이너의 시작부터 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입 : 스프링 컨테이너는 빈의 생성과 의존성 주입까지만 관여하고, 더 이상 관리하지 않는 짧은 범위의 스코프
- 웹 관련 스코프 : request, session, application 등 여러 스코프가 존재한다.
예를 들어, request는 웹 요청이 들어오고 나갈 때까지만 유지되는 스코프이다.
싱글톤 빈 스코프
일반적으로는 다른 설정을 하지 않고 싱글톤 빈으로 이용한다.
스프링 컨테이너가 생성될 때, 싱글톤 빈을 생성하여 스프링 DI 컨테이너에서 관리한다.
빈으로 등록된 객체를 사용하기 위해 스프링 컨테이너에 요청하면, 스프링 컨테이너가 싱글톤으로 관리하는 빈을 반환한다. 싱글톤으로 관리하기 때문에, 같은 요청이 여러 번 와도 같은 스프링 빈 객체 인스턴스를 반환한다.
프로토타입 빈 스코프
프로토타입 스코프로 설정된 빈은 스프링 컨테이너가 빈을 생성하고 의존성 주입까지만 관여하고 더 이상 관리하지 않는다. 따라서 빈 반환 후에 @PreDestroy가 호출되지 않고, 필요시에 클라이언트가 직접 빈을 종료시켜줘야 한다.
프로토타입 스코프로 설정된 빈을 스프링 컨테이너에 요청하면, 이 시점에 스프링 컨테이너가 빈을 생성하고 의존성을 주입하여 클라이언트에 반환한다. 이후에는 스프링컨테이너가 관리하지 않고, 같은 요청이 여러 번 올 경우 그때마다 새로운 객체 인스턴스를 생성하여 반환한다.
빈 스코프 설정 방법
아래와 같이 Bean 등록하는 부분에서 @Scope 어노테이션을 이용해서 설정해 줄 수 있다.
SingletonBean의 경우 @Scope를 생략하면 디폴트로 싱글톤으로 설정된다.
@Scope("prototype")
@Component
public class PrototypeBean{
//...
}
@Configuration
public class AppConfig{
@Scope("prototype")
@Bean
public PrototypeBean prototypeBean(){
return new PrototypeBean();
}
}
프로토타입 빈 의존성 주입 문제
싱글톤 빈이 프로토타입 빈을 의존성 주입받아서 사용하는 경우 의도치 않은 방식으로 동작할 수 있다.
아래 예제 코드를 보자.
(SingletonBean은 싱글톤, PrototypeBean은 프로토타입 스코프이다.)
@RequiredArgsConstructor
@Component
public class SingletonBean{
private final PrototypeBean prototypeBean;
//...
}
SingletonBean이 생성되는 시점에 생성자 주입을 통해 PrototypeBean 의존성을 주입받는다.
이렇게 되면, 하나뿐인 SingletonBean에 PrototypeBean이 주입되고, 이후에 이 PrototypeBean 인스턴스는 그대로 유지된다. 맨 처음 의도했던 것은 PrototypeBean을 사용할 때마다 새롭게 생성되길 바랐을 것이다.
이 문제를 해결하기 위한 방법을 알아보자.
ObjectProvider
Spring에서 제공하는 ObjectProvider를 이용하여 해결할 수 있다.
import org.springframework.beans.factory.ObjectProvider;
@RequiredArgsConstructor
@Component
public class SingletonBean{
private final ObjectProvider<PrototypeBean> prototypeBeanProvider;
public void logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.doSomething();
}
}
위와 같이 코드를 작성한 뒤, getObject()를 호출하면 스프링 컨테이너에서 해당 빈을 찾아서 반환한다.
이 과정을 Dependency Lookup(DL : 의존관계 조회 또는 탐색)이라고 한다.
Provider
JSR-330 자바 표준인 Provider를 이용하는 방법도 있다.
위의 ObjectProvider와 사용 방법이 거의 비슷하지만 Spring에서 제공하는 것이 아니라 자바 표준이라는 점이 다르다.
이 방법은 gradle에 아래 라이브러리를 추가해야 한다.
implementation 'javax.inject:javax.inject:1'
import javax.inject.Provider;
@RequiredArgsConstructor
@Component
public class SingletonBean{
private final Provider<PrototypeBean> prototypeBeanProvider;
public void logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.doSomething();
}
}
거의 비슷하지만, Provider는 get() 메서드를 이용하여 빈을 불러온다.
이것도 마찬가지로 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다. (DL)
웹 스코프와 프록시
가장 넓은 범위에서 사용할 수 있는 Singleton Scope와 가장 좁은 범위에서 사용할 수 있는 Prototype Scope에 대해 알아봤다. 이외에도 여러 범위에서 사용할 수 있는 Bena Scope들이 존재한다.
대표적으로 Web Aware Scope에 해당되는 Request, Session, Application Scope 등이 있다.
이 Scope들은 Prototype Scope와 달리 @PostDestroy가 동작한다.
Request
Bean을 Http Request에 따른 LifeCycle을 갖도록 지정한다.
HTTP 요청이 올 때마다 새로운 빈 인스턴스를 생성하고, 요청 종료 시 삭제한다.
Session
Bean을 Http Session에 따른 LifeCycle을 갖도록 지정한다.
HTTP 세션마다 새로운 빈 인스턴스를 생성하고, 요청 종료 시 삭제한다. 다른 HTTP 요청이라도 같은 Session을 갖고 있다면 같은 빈 인스턴스를 사용한다.
Application
Bean을 ServletContext에 따른 LifeCycle을 갖도록 지정한다. (ApplicationContext가 아닌 ServletContext이다!)
이 Scope는 singleton Scope와 비슷하지만, 조금 다르다.
- singleton Scope : ApplicationContext 내에서 singleton으로 관리
- application Scope : ServletContext 내에서 singleton으로 관리
ApplicationContext와 ServletContext의 차이는 따로 찾아보면 좋을 것 같습니다.
Proxy
이러한 웹 스코프 역시 위에서 언급한 ObjectProvider를 이용하여 사용할 수 있지만, 번거롭게 ObjectProvider를 이용하지 않고 프록시를 이용해서 해결할 수 있다.
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class RequestScopeBean {
//...
}
이렇게 하면 해당 Bean에 대한 가짜 프록시 클래스를 만들어 두고 프록시 클래스를 다른 빈에 미리 주입해둘 수 있다.
CGLIB라는 라이브러리를 이용해서 프록시 기능을 이용한다.
프록시 동작 방식
- CGLIB 라이브러리로 내 클래스를 상속받은 가짜 프록시 객체를 만들어서 주입한다.
- 이 프록시 객체에는 실제 요청이 올 시 내부에서 실제 빈을 요청하는 위임 로직이 들어있다.
- 가짜 클래스를 주입해 두고 실제 사용할 때까지 지연처리 해뒀다가, 진짜 사용할 때 빈을 요청해서 사용하는 것이다.
프로토타입 스코프는 프록시를 이용할 수 없다.
정리
빈 스코프란?
빈 스코프는 빈이 존재할 수 있는 범위를 말한다. 디폴트는 싱글톤으로 설정되어 있다. 싱글톤은 가장 많이 사용되고, 스프링 컨테이너의 시작부터 종료까지 유지되는 가장 넓은 범위의 스코프이다. 다른 빈 스코프로는 Prototype, request, session 등이 있다.
프로토타입 스코프
프로토타입 스코프는 스프링 컨테이너가 빈의 생성과 의존성 주입까지만 관여하고, 더 이상 관리하지 않는, 가장 짧은 범위의 스코프이다. 따라서 스프링 컨테이너가 @PreDestroy를 하지 않고, 빈의 소멸은 클라이언트에서 처리해야 한다는 특징이 있다.
Proxy
프록시 가짜 클래스를 만들어서 의존성을 주입해 둔 뒤, 실제 사용할 때 빈을 요청해서 사용하도록 하는 기술이다. @Scope 어노테이션에서 proxyMode를 설정할 수 있다. prototype Scope에서는 프록시 를 사용할 수 없다.
'JAVA & Spring > Spring' 카테고리의 다른 글
[DB / Spring ] @Transactional 세부 설정 - 격리 수준 / 전파 수준 설정 (0) | 2024.03.19 |
---|---|
[Spring] 빈 생명 주기 콜백 (Bean LifeCycle) @PostConstruct / @PreDestroy (0) | 2024.02.23 |
[Spring] 의존성 주입 방법 (DI : Dependency Injection), 생성자 주입 / 필드 주입 / setter 주입, 생성자 주입을 사용하는 이유 (0) | 2024.02.22 |
[Spring Boot] @RequestMapping 매핑 (0) | 2023.10.02 |
[Spring] application.properties와 application.yml의차이 (0) | 2023.08.18 |