Study/Spring Framework

[Spring] Bean Scope의 개념과 종류

jonghne 2023. 10. 18. 20:20

Bean Scope 란 ?

Bean Scope(빈 스코프)란 Bean으로 등록한 객체가 생성되고 소멸 될 때까지 생존하는 범위를 의미한다.

 

Spring에서 제공하는 Bean Scope는 총 6가지로 Singletone / Prototype / Request / Session / Application / WebSocket 가 있다. 

 

Spring의 기본 Bean Scope는 Singletone이고 원한다면 Bean을 등록할 때 설정을 변경할 수 있다.

 

단, 애플리케이션의 동작과 메모리 사용 등에 큰 영향을 끼치는 설정이기 때문에 주의해서 사용해야 한다.

 

빈 스코프 설정 방법

빈 스코프는 XML 또는 자바 어노테이션으로 지정할 수 있는데 예시는 아래와 같다. 

 

📌 XML 방식

<bean id="myBean" class="com.example.MyBean" scope="prototype">
    <!-- 빈 설정 정보 -->
</bean>

 

📌 자바 어노테이션 방식

@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public MyBean myBean() {
        // 빈 설정 정보
        return new MyBean();
    }
}

 

Bean Scope) 종류 

Singletone Scope

스프링 프레임워크의 기본 스코프로, 스프링 컨테이너가 기동 될 때 부터 종료 될 때까지 유지되는 가장 넓은 범위의 스코프이다.

 

이 싱글 톤 스코프는 등록한 Bean 별로 딱 1개의 인스턴스만을 생성하고, Bean을 요청할 때 마다 항상 같은 인스턴스를 반환한다. 

 

Prototype Scope

Prototype Scope는 해당 Bean을 사용할 때 마다 매번 새로운 Bean 인스턴스를 생성하는 매우 짧은 범위의 스코프로, Spring Container는 빈의 생성과 의존관계 주입 그리고 초기화까지만 관여하고 그 이후로는 더는 관여하지 않는다

 

아래의 그림과 같이 Prototype Scope으로 설정한 객체는 Spring Container에서 사용할 때 마다 항상 새로운 인스턴스를 생성한다.

 

초기화 된 이후 스프링이 관리하지 않기 때문에, 스프링 컨테이너가 종료될 때 @PreDestroy 같은 소멸작업도 호출되지 않는다.

 

만약 Bean 소멸 전에 처리해야 하는 작업이 있다면 직접 소멸 메서드를 호출 하는 방식으로 구현해줘야 한다.

 

📌  프로토타입 빈을 싱글톤 빈과 함께 사용 시 문제점 

싱글 톤 빈(Bean)의 내부 필드에 프로토타입 빈을 함께 사용한다면, 싱글 톤은 하나의 인스턴스만 생성되기 때문에 프로토타입의 빈을 사용할 때 새로 생성되는 것이 아닌 동일 인스턴스를 계속 재사용한다는 문제가 있다.

 

즉, 싱글톤 빈이 생성될 때 의존관계 주입 시점에 프로토타입 빈이 새로 생성되긴 하지만, 싱글톤 빈 내부에 있기 때문에 싱글톤 빈과 계속 유지된다.

 

📌 프로토 타입 빈을 싱글톤 빈과 함께 사용하는 방법 (Provider)

1. ObjectProvider, ObjectFactory 사용

프로토 타입의 특징을 살리면서 싱글톤 빈과 같이 사용하고 싶은 경우 스프링에서 제공하는 ObjectProvider, ObjectFactory를 사용하면 된다.

 

이 기능은 스프링 컨테이너에서 의존 관계를 찾을 수 있는 (DL) 기능을 제공한다. (프로토타입 빈을 위한 기능이 아닌 단순히 컨테이너에서 빈을 찾아서 반환하는 기능이다)

 

ObjectFactory는 기능이 단순하고 별도의 라이브러리가 필요없지만 스프링에 의존한다는 특징이 있다.

 

ObjectProvider는 ObjectFactory을 상속한 것으로 다양한 편의 기능을 사용할 수 있다 (옵션, 스트림처리 등)

 

2. JSR-330 Provider 사용

자바 표준 라이브러리로 스프링 컨테이너를 통해 빈을 찾아서 반환하는 기능 (DL)이다.

 

get() 메소드 하나만 있어서 기능이 매우 단순하고, 자바 표준이기 때문에 스프링이 아닌 다른 컨테이너에서도 사용 가능하다는 장점이 있다.

 

하지만 라이브러리를 추가해야한다는 단점이 있다. 

 

3. 웹 관련 스코프

1) request

HTTP Request 요청이 올 때 마다, 해당 Bean의 인스턴스가 새로 생성되고 응답 이후 소멸되는 스코프이다.

2) session

HTTP Session이 생성될 때 마다 해당 Bean의 인스턴스가 생성되고, 세션이 종료되면 소멸되는 스코프이다.

3) application

Sevlet Context가 시작할 때 해당 Bean의 인스턴스가 생성되고, 종료될 때 같이 소멸되는 스코프이다.

4) websocket

WebSocket이 열릴 때 해당 Bean의 인스턴스가 생성되고, 연결이 닫힐 때 소멸되는 스코프이다.

 

📌 웹 관련 스코프 흐름도 (request 예시)

 

※ 사용 시 주의사항

웹 스코프는 웹 요청이 들어왔을 때 빈이 생성되기 때문에, 다른 Controller, Service에서 주입시켜놓으면 오류가 발생한다.

 

이 때 Provider 또는 Proxy를 사용해서 진짜 객체 조회를 필요한 시점까지 지연처리 하도록 구현하면 된다.

 

(1) Provider로 DL (의존관계 찾기) 해서 빈을 찾아오기

ObjectProvider로 감싸서 객체를 주입해놓고, 실제 사용 하는 부분에서 DL로 빈을 찾아온다

@Service
 @RequiredArgsConstructor
 public class LogDemoService {
     private final ObjectProvider<MyLogger> myLoggerProvider;
     public void logic(String id) {
         MyLogger myLogger = myLoggerProvider.getObject();
         myLogger.log("service id = " + id);
} }

 

 

(2) Proxy (CGLIB을 사용해 내 클래스를 상속받은 가짜 프록시 객체 만들어서 주입)

등록하려는 빈에 @Scope 어노테이션을 추가하고 proxyMode 설정을 지정한다. 

@Component
 @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
 public class MyLogger {
}

 

이렇게 등록 된 Bean은 CGLIB 라이브러리를 사용한 가짜 프록시 객체이다.  

가짜 프록시 객체는 실제 요청이 오면 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.

 

이렇게 어노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체할 수 있는 점은 다형성 + DI컨테이너가 가진 큰 강점이다. (이 Proxy는 웹 스코프가 아니어도 사용가능)

 

동작 원리는 아래와 같다

 

 

 

출처 : 김영한님, 스프링 핵심 원리 기본편