웹 프로그래밍/[ Spring Boot ]

[ SpringBoot ] 03. Spring의 Bean Scope를 알아보자

kim.svadoz 2021. 10. 12. 16:55
반응형

Bean Scope

Bean을 정의할 때 해당 빈에 정의된 클래스의 실제 인스턴스를 생성하기 위한 방법을 생성한다.

단일 레시피로 많은 객체 인스턴스를 만들 수 있기 때문에 Bean Definition에 관한 개념은 매우 중요하다.

스프링은 기본적으로 모든 Bean을 Singleton으로 생성해 관리한다.

구체적으로는 애플리케이션 구동시 JVM 안에서 스프링이 bean마다 하나의 객체를 생성하는 것을 의미하고, 우리는 스프링을 통해서 bean을 제공받으면 주입 받은 bean은 언제나 동일한 객체임을 가정하고 개발한다.

특정 Bean Definition에서 생성된 객체에 연결될 다양한 종속성과 구성을 제어할 수 있을 뿐만 아니라, 특정 Bean Definition에서 생성된 객체의 범위도 제어할 수 있다.

1. singleton

singleton bean은 스프링 컨테이너에서 단 한 번 생성이되고 컨테이너가 종료될 때 bean도 소멸된다.

생성된 하나의 인스턴스는 single beans cache에 저장되고, 해당 bean에 대한 요청과 참조가 있으면 캐시된 객체를 반환한다. 즉, 하나만 생성되기 때문에 모두 동일한 것을 참조한다.

기본적으로 모든 bean은 scope를 명시적으로 지정하지 않으면 singleton이다.

<bean id="accountService" class="com.something.DefaultService" scope="singleton"/>
@Scope("singleton")

추가적으로, 스프링에서 말하는 싱글톤은 디자인패턴의 싱글톤패턴과 다르다.

싱글톤패턴은 하드코딩된 객체(classloader)가 singleton 객체를 관리하므로 사용시 유의해야 할 점이 있다.

singleton 객체가 전역변수와 같은 역할 때문에 coupling이 높아서 테스트에 어령무이 있고 애플리케이션의 의존성 측면에서도 singleton 객체를 사용하는 쪽에서 singleton 객체에 대해 너무 많은 정보를 알아야 한다는 설계상의 문제가 있다.(참고)

2. prototype

prototype bean은 모든 요청에 대해 새로운 객체를 생성한다.

즉, 의존성 관계의 bean에 주입될 때 새로운 객체가 생성되어 주입되고 정상적인 방식으로 gc에 의해 bean이 소멸된다.

일반적으로 모든 Stateful Bean에는 프로토타입 scope을 사용하고, Stateless Bean에는 싱글톤 scope을 사용해야 한다.

Spring은 prototype bean의 생명 주기를 관리하지 않는다. 컨테이너는 프토토타입 객체를 인스턴스화 해서 클라이언트에 전달한다.

따라서 초기화에 대한 lifecycle callback method는 scope에 관계없이 모든 객체에 대해 호출되지만 prototype의 경우 소멸 lifecycle callback은 호출되지 않는다.

그러므로 클라이언트 코드는 프로토타입 scope 인스턴스를 정리하고 프로토타입 빈이 보유하고 있는 resource를 해제해주어야 한다. 해당 빈에 대한 참조를 보유하고 있는 Custom Bean Post Processor를 이용해서 프로토타입의 빈 리소스를 해제할 수 있다.

<bean id="accountService" class="com.something.DefaultService" scope="prototype"/>
@Scope("prototype")

3. singleton with prototype-bean dependencies

프로토타입 빈에 대한 종속성이 있는 싱글톤 빈은, 인스턴스시킬 때 해결된다.

프로토타입의 빈을 -> 싱글톤 빈으로 의존성을 주입하면 새로운 프로토타입 빈이 인스턴스화되고 싱글톤 빈에 종속성이 주입된다. 따라서 이 프로토타입 인스턴스는 싱글톰 빈에 제공되는 유일한 인스턴스이다.

Spring컨테이너는 싱글톤 빈을 인스턴스화하고 의존성을 해결하고 주입할 때 이 DI가 한번만 발생하기 때문에, 프로토타입 빈을 반복적으로 싱글톤 빈에 종속할 수 없다. 런타임시 프로토타입 빈의 새로운 인스턴스가 두 번이상 필요하다면 Method Injection을 사용하자.

4. Request, Session, Application, WebSocket Scopes

request, session, application, websocket scope는 스프링의 ApplicationContext의 구현체를 사용하는 경우에만 사용가능하다.(XmlWebApplicatoinContext) 즉, Spring MVC Web Application에서만 사용되는 용도이다.

이러한 Bean scope를 일반적인 Spring IoC 컨테이너(ClassPathXmlApplicationContext)와 함께 사용하면 IllegalStateException과 같은 알수없는 예외가 발생한다.

  • request : HTTP 요청 별로 인스턴스화 되며 요청이 끝나면 소멸된다.
  • session : HTTP 세션 별로 인스턴스화 되며 세션이 끝나면 소멸된다.

> singleton vs non-singleton

싱글톤과 비싱글톤을 구분해서 빈을 선언하는 기준의 핵심은 수정 가능한 상태에 따른 동기화 비용과 객체 생성 비용간의 트레이드 오프를 따져 보라는 의미

  • 싱글톤으로 적합한 객체
    • 상태가 없는 공유 객체
    • : 상태를 가지고 있지 않은 객체는 동기화 비용이 없어 매번 객체를 참조하는 곳에서 새로운 객체를 생성할 이유가 없다.
    • 읽기용으로만 상태를 가진 공유 객체
    • : 1번과 유사하개 상태를 가지고 있으나 읽기전용이므로 여전히 동기화 비용이 들지 않는다. 따라서 매 요청마다 새로운 객체를 생성할 필요가 없다.
    • 공유가 필요한 상태를 지닌 공유 객체
    • : 객체간에 반드시 공유해야 할 상태를 지닌 객체가 하나 있다면, 이 경우에는 해당 상태의 쓰기를 가능한 동기화할 경우 싱글톤도 적합하다.
    • 쓰기가 가능한 상태를 지니면서도 사용빈도가 매우 높은 객체: 이 방법은 1.장시간에 걸쳐 매우 많은 객체가 생성되고, 2.해당 객체가 매우 작은 양의 쓰기상태를 가지고 있고, 3.객체 생성비용이 매우 클때 유용한 선택이 된다.
    • : 애플리케이션에서 사용빈도가 높다면 쓰기 접근에 대한 동기화 비용을 감안하고서라도 싱글톤을 고려할만 하다.
  • 비싱글톤(ex. 프로토타입)으로 적합한 객체
    • 쓰기가 가능한 상태를 지닌 객체
    • : 쓰기가 가능한 상태가 많아서 동기화 비용이 객체 생성 비용보다 크다면 싱글톤으로 적합하지 않다.
    • 상태가 노출되지 않는 객체
    • : 일부 제한적인 경우, 내부 상태를 외부에 노출하지 않는 빈을 참조하여 다른 의존객체와는 독립적으로 작업을 수행하는 의존객체가 있다면 싱글톤보다 비싱글톤 객체를 사용하는 것이 더 좋을수도 있다.

참조 : https://gmlwjd9405.github.io/2018/11/10/spring-beans.html

반응형