개발 기록

> [Spring] Bean Scope - 1. 개념과 사용 방법(singleton, prototype, request) 본문

Spring

> [Spring] Bean Scope - 1. 개념과 사용 방법(singleton, prototype, request)

1z 2024. 1. 18. 13:01

 

1. 개념  

Bean 이 존재할 수 있는 범위. 기본적으로 스프링의 빈 범위는 싱글톤이다. 

 

 

(1) Spring 기본 스코프 

scope 설명
singleton Spring IoC 컨테이너당 단일 빈 정의에 대해 하나의 인스턴스만 생성되며 해당 빈에 대한 각 요청에 대해 동일한 객체 공유
prototype 해당 Bean에 대한 요청이 이루어질 때마다 단일 Bean 정의에 대한 새 인스턴스 생성
request 단일 Bean 정의에 대해서 하나의 HTTP request 라이프사이클 안에 단 하나의 객체만 존재한다.
- 각각의 HTTP request는 자신만의 객체를 가진다. 

- 해당 Bean에 대해 HTTP 요청이 이루어질 때마다 단일 Bean 정의에 대한 새 인스턴스가 생성
session 단일 Bean 정의에 대해서 하나의 HTTP Session 라이프사이클 안에 단 하나의 객체만 존재한다.
application 단일 Bean 정의의 범위를 의 ServletContext 라이프사이클로 지정
websocket 단일 Bean 정의의 범위를 WebSocket 라이프사이클로 지정

 

request, session, application  및 websocket scope 는 web-aware Spring ApplicationContext 에서만 유효하다. request, session, application 세 개를 묶어서 웹 스코프 라고도 한다.

 

 

기본 6가지 Scope 중 singleton, protype, request socpe 를 사용해본다.


 

1. 싱글톤(Singleton) 스코프 

해당 Bean의 인스턴스 하나만 Spring IoC 컨테이너별로 인스턴스화되고 각 요청에 대해 동일한 인스턴스가 공유된다.

1. 해당 Bean에 대한 새 요청이 이루어질 때마다 Spring IOC 컨테이너가 먼저 해당 Bean의 인스턴스 생성 여부 확인
2-1. 이미 생성된 경우 IOC 컨테이너는 동일한 인스턴스를 반환
2-2. 생성된 bean 이 없을 경우 해당 Bean의 새 인스턴스를 생성

 

 

* Spring의 싱글톤 빈 개념은 GoF(Gang of Four)  정의된 싱글톤 패턴과 다르다.

Spring은 컨테이너당 주어진 bean id 정의에 대해 단 하나의 bean 인스턴스 생성을 보장하고,  Singleton 패턴은 ClassLoader당 단 하나의 인스턴스가 생성되도록 보장한다.

 

 

(1) 구현

 

@Scope 어노테이션에 싱글톤 범위 로 Bean을 정의한다.

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public Class User {
	String name;
}

 

  ATest, BTest 에 각각 User Bean 의존성을 주입한다. 

@Component
public class ATest {
    @Autowired User user;
}

----------------------------------------------------

@Component
public class BTest {
    @Autowired User user;
}

 

출력 결과를 확인해보면 처음 요청을 할 때 Bean의 새 인스턴스( User )가 생성되고 각각의 새 요청에 대해 동일한 객체가  'Atest' 와 'Btest' 간에 공유되고 있다는 것을 알 수 있다.

@ActiveProfiles(value = "local")
@SpringBootTest
public class BeanTest {

    @Autowired ATest atest;

    @Autowired BTest btest;

    @Test
    public void singleton_bean_test() {
        atest.beanConfig.text = "John Smith";
        btest.beanConfig.text = "kim_John Smith";

        System.out.println(atest.beanConfig.text); // 출력결과 => John Smith   
        System.out.println(btest.beanConfig.text); // 출력결과 => kim_John Smith
        System.out.println(atest.beanConfig.text); // 출력결과 => kim_John Smith
    }

 

 

(2) 싱글톤으로 적합한 객체

상태가 없는 공유 객체

: 동기화 비용이 들지 않기 때문에 매번 이 객체를 참조하는 곳에서 새로운 객체를 생성할 이유가 없다.

읽기용으로만 상태를 가진 공유 객체

: 동기화 비용이 들지 않기 때문에 매 요청마다 새로운 객체 생성할 필요가 없다.

공유가 필요한 상태를 지닌 공유 객체

쓰기가 가능한 상태를 지니면서도 사용빈도가 매우 높은 객체

: 애플리케이션 안에서 정말로 사용빈도가 높다면, 쓰기 접근에 대한 동기화 비용을 감안하고서라도 싱글톤을 고려할만하다. 이 방법은 장시간에 걸쳐 매우 많은 객체가 생성될 때, 해당 객체가 매우 작은 양의 쓰기상태를 가지고 있을 때, 객체 생성비용이 매우 클 때에 유용한 선택이 될 수 있다.

 

(3) 비싱글톤으로 적합한 객체 

쓰기가 가능한 상태를 지닌 객체

: 쓰기가 가능한 상태가 많아서 동기화 비용이 객체 생성 비용보다 크다면 싱글톤으로 적합하지 않다.

상태가 노출되지 않은 객체

: 내부 상태를 외부에 노출하지 않는 빈을 참조하여 다른 의존객체와는 독립적으로 작업을 수행하는 의존 객체가 있을 경우

 

 


 

2. 프로토 타입 (Prototype) 스코프 

스프링 IOC 컨테이너는 해당 특정 Bean에 대한 요청이 이루어질 때마다 해당 Bean 의 새 인스턴스를 생성한다.

즉 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입 그리고 초기화까지만 관여하게 된다. 그래서 @PreDestory 같은 종료 메서드가 자동으로 호출되지 않기 때문에 해당 시점 이후의 모든 수명주기 관리는 사용자가 처리해야 한다.

 

 

* 일반적으로 Stateful Bean에는 프로토타입 범위를 사용하고 Stateless Bean에는 싱글톤 범위를 사용한다. 

* getBean() 메소드뿐만 아니라 @Autowired와 같은 DI 선언도 각각 독립적인 빈 요청이므로 매번 새로운 오브젝트가 만들어져서 주입된다.

 

(1) 구현

 @Scope 어노테이션에 프로토타입을 설정하여 정의한다.

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Class User() {
  String name;
}

 

아래를 보면  User bean 인스턴스를 참조한 'ATest', 'BTest'  가 각각 User 의 이름을 변경했을 때 서로 다른 값을 출력 한것을 확인 할수 있다.

@ActiveProfiles(value = "local")
@SpringBootTest
public class BeanTest {

    @Autowired ATest atest;

    @Autowired BTest btest;

    @Test
    public void singleton_bean_test() {
        atest.user.name = "John Smith";
        btest.user.name = "kim_John Smith";

        System.out.println(atest.beanConfig.text); // 출력결과 => John Smith
        System.out.println(btest.beanConfig.text); // 출력결과 => kim_John Smith
        System.out.println(atest.beanConfig.text); // 출력결과 => John Smith
    }

 

 

아래 결과로 'ATest', 'BTest'  가 서로 다른 개체를 참조하고 있음을 볼 수 있다. 즉 이 Bean에 대한 요청이 있을 때마다 새로운 Bean 인스턴스( User) 가 생성됬다는 것이다.

 

 

(2) Protype Scope Bean 을 사용하는 경우 

  • 코드에서 new로 오브젝트를 생성하는 것을 대신하기 위해 사용
  • 대부분의 경우 new로 오브젝트를 생성하거나 팩토리를 이용해서 오브젝트를 생성하면 된다. 하지만 컨테이너의 DI 기능을 사용해야 하고 싶은 경우는 프로토타입 스코프 빈을 사용한다.
  • 매번 새로운 오브젝트가 필요하면서 DI를 통해 다른 빈을 사용해야할 경우 프로토타입 빈이 적절한 선택이다.
  • new 키워드를 대신하기 위해 사용되는 것이 프로토타입의 용도라고 본다면, DI는 프로토타입 빈을 사용하기에 적합한 방법이 아니다. DI가 아닌 DL 방식으로 사용해야한다.

 


 

3. Request 스코프 

단일 HTTP 요청에 대한 Bean 인스턴스를 생성하고 요청 처리가 완료되면 해당  Bean은 삭제 된다.

ex. Bean마다 초기화 콜백 메소드로 uuid를 생성하여 사용하면 다른 HTTP 요청과 구분할 수 있다.

 

 

(1) 구현

 

ProxyMode 속성 설정으로 request scope type 빈 지연 생성

★ 스프링 빈 등록 시 웹 스코프를 그대로 주입받으면 오류가 발생하는데  싱글톤 빈은 스프링 컨테이너와 라이프 사이클을 같이하지만, 웹 스코프(request, session 등)의 경우 HTTP 요청이 올 때 새로 생성되고 응답하면 사라지기 때문에, 싱글톤 빈이 생성되는 시점에는 아직 생성되지 않기 때문이다.

 

이때 프록시를 사용하면 문제를 해결할 수 있다.

- proxyMode = ScopedProxyMode.TARGET_CLASS 속성 활용

스프링 컨테이너에 등록된 가짜 객체인 프록시 객체가 생성되어 빈으로 등록된 후에, 실제 요청이 오면  Bean을 인스턴스화하고 해당 빈이 실제 사용될 때 프록시 빈에서 실제 빈을 가져와 사용할 수 있도록 한다.

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public Class User() {
   String name;
}

 

 

 위 프록시 모드 설정을 아래와 같이 설정해도 같은 효과를 낸다.

@Bean
@RequestScope
public Class User() {
   String name;
}

 

 

User 빈을 주입한 컨트롤러 정의

@RestController
public class ScopeController {
    @Autowired
    User user;

    @GetMapping("/scopes/request")
    public  Map<String, String> getRequestScopeMessage() {

        Map<String, String> response = new HashMap<>();
        response.put("previous name ",beanConfig.text);
        user.name="홍길동";
        response.put("current name",beanConfig.text);
        return response;
    }
}

 

요청 마다 다른 Bean 인스턴스를 반환하기 메서드에서 값이 변경되더라도 null 로 재설정되는 것을 볼 수 있다.

 [{current name=홍길동, previous name =null}]

 

 

 

 

 

 

 

참고

 https://maenco.tistory.com/entry/Spring-Container스프링-컨테이너-Bean]

https://0soo.tistory.com/225

https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html

https://www.baeldung.com/spring-bean-scopes