빈 스코프 정의
- 스프링 빈이 존재할 수 있는 범위 즉, 생성 및 소멸하는 구간
스프링에서 지원하는 빈 스코프
- 싱글턴 (Singleton): Default 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입 (Prototype): 스프링 컨테이너가 프로토타입 빈의 생성과 의존관계 주입 그리고 초기화까지만 관여, 그 이상 관리하지 않는 짧은 범위의 스코프
- 웹 관련 스코프
- request: 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
- session: 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
- application: 웹의 ServletContext와 같은 범위로 유지되는 스코프
- websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프
* 싱글턴의 경우 클래스의 인스턴스가 오직 1개만 생성되는 것을 보장하는 디자인 패턴이기 때문에 매 요청마다 같은 객체를 반환 (https://jaimemin.tistory.com/1756 참고)
* 프로토타입의 경우 매 요청마다 항상 새로운 인스턴스를 생성한 뒤 반환 즉, 매번 다른 인스턴스를 반환
프로토타입 예제 코드
프로토타입 빈의 특징
- 스프링 컨테이너에 요청할 때마다 새로 생성
- 스프링 컨테이너는 프로토타입 빈의 생성, 의존관계 주입, 그리고 초기화까지만 관여 즉, 소멸 관리 안 하므로 @preDestroy 메서드 호출 안됨
- 따라서, 해당 인스턴스를 소멸시키기 위해서는 명시적으로 destroy 메서드를 호출해야 함
싱글턴 빈과 프로토타입 스코프를 함께 사용할 때 발생할 수 있는 문제점
- 싱글턴 빈은 인스턴스가 오직 1개만 생성되므로 의존관계 주입을 단 한 번만 받음
- 따라서, 싱글턴 빈이 프로토타입 빈을 필드로 가지고 있더라도 한번 의존관계 주입을 받은 이상 해당 필드(프로토타입 빈)는 같은 인스턴스
- 정리를 하자면, 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성이 된 것이기 때문에 사용할 때마다 새로 생성되는 것이 아님
- 실제 원하는 바는 주입받는 시점에 각각 새로운 프로토타입 빈이 생성되는 것!
문제가 발생하는 코드
지저분하지만 문제를 해결할 수 있는 코드
- 매번 스프링 컨테이너에 새로 요청하는 방식
- 이렇게 직접 필요한 의존관계를 찾는 것을 Dependency Lookup (DL)이라고 함
Provider를 통해 해결하는 코드
- DI 서비스를 제공하는 ObjectProvider가 존재 (이를 통해 기존의 지저분한 코드 해결 가능)
- Java 표준에는 동일한 기능을 하는 javax.inject.Provider 존재 (제공하는 기능은 적지만 스프링뿐만 아니라 다른 컨테이너에서도 사용 가능하므로 조금 더 범용적)
* ObjectProvider의 getObject 메서드를 호출하여 스프링 컨테이너를 통해 해당 빈을 찾아서 반환 가능 (DL)
웹 스코프
- 웹 스코프는 웹 환경에서만 동작
- 프로토타입과 달리 스프링 컨테이너가 해당 스코프의 종료 시점까지 관리 즉, @preDestroy 메서드가 호출됨
- 웹 스코프의 경우 기본적으로 비슷하게 동작하므로 request scope를 기준으로 예제를 만들었습니다.
request scope 사용 예시
- HTTP request 당 인스턴스가 하나씩 생성되고, HTTP request가 끝나는 시점에 소멸됨
- 따라서, 동시에 여러 HTTP request가 왔을 때 어떤 request가 남긴 로그인지 판별할 때 사용
- 주의할 점: request scope는 HTTP request가 들어와야 생성되기 때문에 싱글턴 빈 의존관계 주입 시점에는 존재하지 않음 (Compile Error 원인)
- 따라서, 앞서 언급한 ObjectProvider나 프록시를 통해 DL을 수행하고 사용되는 시점에 객체 주입
컴파일 에러가 발생하는 코드
* request scope는 HTTP request가 들어와야 생성되기 때문에 싱글턴 빈 의존관계 주입 시점에는 존재하지 않음
ObjectProvider를 통해 해결한 코드
* ObjectProvider 덕분에 ObjectProvider.getObject() 메서드를 호출하는 시점까지 request scope 빈의 생성 지연 가능
* ObjectProvider.getObject() 메서드를 호출하는 시점에는 HTTP request가 진행 중이므로 정상 처리
Proxy를 통해 해결한 코드
* 적용대상이 인터페이스면 ScopedProxyMode.INTERFACES 선택, 클래스라면 scopedProxyMode.TARGET_CLASS 선택
* CGLIB 바이트 코드 조작 라이브러리를 통해 가짜 프록시 클래스를 의존관계 주입할 때 미리 주입한 후 가짜 프록시 객체에 요청이 오면 진짜 빈을 요청하여 위임 (CGLIB 라이브러리 내 로직이 존재, 다형성의 위대함)
* 가짜 프록시 객체는 실제 request scope와 관계없이 내부에 단순한 위임 로직만 존재하고 싱글턴 빈처럼 동작
* Provider를 사용하던 Proxy를 사용하던 핵심은 진짜 객체 조회를 꼭 필요한 시점까지 지연 처리시킨다는 점
참고
인프런 스프링 핵심 원리 - 기본편 (김영한 강사님)
'Spring' 카테고리의 다른 글
Spring MVC 구조 정리 (0) | 2021.06.09 |
---|---|
클라이언트에서 서버로 HTTP 요청 메시지 보내는 방법 (0) | 2021.06.02 |
스프링 빈 생명주기 (Spring Bean Life Cycle) (0) | 2021.05.25 |
@Autowired - 의존관계 주입 (2) | 2021.05.18 |
@ComponentScan - 컴포넌트 스캔 (0) | 2021.05.17 |