Spring

빈 스코프 (Bean Scope)

꾸준함. 2021. 5. 26. 03:14

빈 스코프 정의

  • 스프링 빈이 존재할 수 있는 범위 즉, 생성 및 소멸하는 구간

 

스프링에서 지원하는 빈 스코프

  1. 싱글턴 (Singleton): Default 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  2. 프로토타입 (Prototype): 스프링 컨테이너가 프로토타입 빈의 생성과 의존관계 주입 그리고 초기화까지만 관여, 그 이상 관리하지 않는 짧은 범위의 스코프
  3. 웹 관련 스코프
    1. request: 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
    2. session: 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
    3. application: 웹의 ServletContext와 같은 범위로 유지되는 스코프
    4. 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를 사용하던 핵심은 진짜 객체 조회를 꼭 필요한 시점까지 지연 처리시킨다는 점

 

참고

인프런 스프링 핵심 원리 - 기본편 (김영한 강사님)

반응형