빈 스코프는 번역 그대로 빈이 존재할 수 있는 범위를 의미합니다.
기본적으로 스프링 빈은 스프링 컨테이너의 시작과 함께 생성되어서 스프링 컨테이너가 종료될 때까지 유지된다고 배웠습니다. 이러한 이유는 @Configuration 에서 배웠듯이 스프링 빈은 기본적으로 싱글톤 스코프로 생성되기 때문입니다.
[Spring] @Configuration <- 다시 공부해보기
스프링은 기본적으로 다음과 같은 빈 스코프들을 지원합니다.
싱글톤 빈은 익숙하니 프로토타입 스코프 부터 알아봅시다.
싱글톤 스코프의 빈은 스프링 컨테이너에 해당 객체를 상속받은, CGLIB에 의해 조작된(?) 객체가 싱글톤을 유지할 수 있도록 관리한다고 했었습니다. 따라서 클라이언트들이 해당 빈을 조회하면 모두 같은 인스턴스의 동일한 빈을 반환받게 됩니다.
그러나 프로토타입 빈의 경우 클라이언트의 요청마다 스프링 컨테이너가 각각 새로운 인스턴스를 생성해서 반환해줍니다.
- 프로로타입 스코프의 빈을 스프링 컨테이너에게 요청한다.
- 요청이 오는 시기에 프로토타입 빈을 생성하고, 의존관계를 주입하고 클라이언트에게 반환한다.
- 이후 같은 요청이 들어오면 다른 빈을 새로 생성해서 반환한다.
💡 여기서 핵심은 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입, 초기화 까지만 관여하고 이후로는 생성된 프로토타입 빈을 관리하지 않습니다.
이후 반환된 빈에 대한 관리의 책임은 클라이언트에게 있게 되고, 당연하게도 @PreDestroy 같은 해당 빈의 종료 메소드는 호출되지 않습니다.
우선 예상되는 두 스코프의 차이는 싱글톤 스코프는 스프링 컨테이너가 올라올 때 시작해서 종료할 때 끝나지만, 프로토타입은 위에서 설명한대로 클라이언트가 요청할 때 마다 각각 생성되고, 생성 - 의존관계 주입 - 초기화 이후로는 관리 권한이 클라이언트에게 넘어가 종료 메소드는 호출 되지 않을 것으로 예상 가능합니다.
코드를 통해 두 스코프의 차이점을 확인해 봅시다.
💡 Scope는 @Scope 어노테이션을 통해 해당 빈의 스코프를 지정할 수 있고, default 는 singleton 입니다.
싱글톤 스코프는 예사대로
프로토타입 스코프도 예상했던 것 처럼
프로토 타입 빈의 특징 정리
스프링 컨테이너에 요청 할 때마다 새로 생성된다.
스프링 컨테이너는 프로토타입 빈의 생성, 의존관계 주입, 초기화 까지만 관여한다.
종료 메소드가 호출되지 않는다.
프로토타입 빈은 프로토타입 빈을 조회한 클라이언트가 관리해야한다. 종료 메소드에 대한 호출도 클라이언트가 직접 해야한다.
프로토타입 스코프의 빈은 요청할 때마다 항상 다른 객체 인스턴스를 생성해서 반환해 줍니다.
그러나 싱글톤 빈과 함께 사용할 때는 의도한 대로 잘 동작하지 않을 수 있어 주의해야 합니다.
위의 그림을 보면 클라이언트 A와 B가 각각 addCount()를 통해 프로토타입 빈의 count를 늘리려고 시도하는데, 프로토타입 빈의 특성상 A와 B에게 각각 생성되어 count 값이 1이 되는것을 기대하지만, 1이 아닌 2를 값으로 가지게 됩니다.
스프링은 일반적으로 싱글톤 빈을 사용하기 때문에 싱글톤 빈이 프로토타입 빈을 사용하게 됩니다. 여기서 문제가 발생하는데, 싱글톤은 생성과 함께 의존관계 주입을 받기 때문에, 그 순간 프로토타입의 빈이 새로 생성되기는 하지만, 싱글톤 빈과 함께 계속유지되는 것이 문제입니다.
💡 같은 프로토타입 빈을 여러개의 빈이 주입받는다면, 각 빈마다 다른 프로토타입 빈이 생성되어 주입됩니다. 그러나 이 또한 사용할 때 새로 생성되지 않고 동일한 프로토 타입 빈을 사용하게 됩니다.
그렇다면 프로토타입 빈을 사용할 때 마다 새로 생성해서 사용하려면 어떻게 해야할까요??
이러한 문제를 해결 시켜주는 것이 Provider 입니다.