본 포스팅은 학습용으로, 김영한 님의 강의를 바탕으로 복습용도로 작성되었습니다.
스프링 컨테이너에 프로토타입 스코프의 빈을 요청하면 항상 새로운 객체 인스턴스를 생성해서 반환한다.
그렇다면 싱글톤 빈에서 프로토 타입 빈을 사용하는 경우는 어떻게 동작할까?
싱글톤 빈에서 프로토타입 빈 사용
싱글톤 빈에서 프로토타입 빈을 사용하면 다음과 같은 문제가 발생한다.
- clientBean은 싱글톤이므로, 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생
- clientBean은 의존관계 자동 주입을 사용한다. 주입 시점에 스프링 컨테이너에 프로토타입 빈을 요청한다.
- 스프링 컨테이너는 프로토타입 빈을 생성해서 clientBean에 반환한다. 프로토타입 빈을 요요청한다.
이제 clientBean은 프로토타입 빈을 내부 필드에 보관한다. (참조값 보관)
- 클라이언트 A는 clientBean을 스프링 컨테이너에 요청해서 받는다. 싱글톤이므로 항상 같은 clientBean이 반환된다.
- 클라이언트 A는 clientBean.logic 함수를 호출한다.
- clientBean은 prototypeBean의 addCount( )를 호출해서 프로토타입 빈의 count를 증가한다. count = 1
- 클라이언트 B는 clientBean을 스프링 컨테이너에 요청해서 받는다. 싱글톤이므로 항상 같은 clientBean이 반환된다.
여기서 핵심! cilentBean은 내부에 가지고 있는 프로토타입 빈은 이미 과거에 주입이 끝난 빈이다. 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성된 것이지, 사용할 때마다 새로 생성되는 것은 아니다.!
- 클라이언트 B는 clientBean.logic을 호출한다.
- clientBean은 prototypeBean의 addCount( )을 호출해서 프로토타입 빈의 count를 증가한다.
원래 count = 1 이었으므로, count = 2가 된다.
스프링은 일반적으로 싱글톤 빈을 사용하므로, 싱글톤 빈이 프로토타입 빈을 사용하게 된다.
하지만, 싱글톤 빈은 생성 시점에만 의존관계 주입을 받기 때문에, 프로토타입 빈이 새로 생성되기는 하지만, 싱글톤 빈과 함께 계속 유지되는 것이 문제이다.
프로토타입 스코프 - 싱글톤 빈과 함께 사용시 Provider로 문제해결
ObjectFactory, ObjectProvider
지정한 빈을 컨테이너에 대신 찾아주는 DL (Dependency Lookup) 서비스를 제공하는 것이 바로 ObjectProvider이다. 과거에는 ObjectFactory였지만, 여기에 기능을 추가해서 ObjectProvider가 만들어졌다.
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
- prototypeBeanProvider.getObject( ) 을 통해 항상 새로운 프로토타입 빈이 생성되는 것을 확인 할 수 있다.
- ObjectProvider의 getObject( )를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다 → DL
- 스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기 위해 훨씬 쉬워짐
- ObjectProvider는 지금 딱 필요한 DL정도의 기능만 제공
- ObjectProvider는 ObjectFactory 상속, 옵션, 스트림 처리 등의 편의 기능이 많고, 별도의 라이브러리가 필요 없고, 스프링에 의존한다.
JSR-330 Provider
javax.inject.Provider라는 JSR-330 자바 표준을 사용하는 방법
스프링 부트 3.0은 jakarta.inject.Provider 를 사용한다.
라이브러리 Gradle 추가
- 스프링부트 3.0 미만
implementataion 'javax.inject:javax.inject:1'
- 스프링부트 3.0 이상
implementataion 'jakarta.inject:jakarta.inject-api:2.0.1'
- 스프링부트 3.0 미만 코드
package javax.inject;
public interface Provider<T> {
T get();
}
@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
- provider.get( )을 통해 항상 새로운 프로토타입 빈이 생성되는 것을 확인할 수 있다.
- provider.get( )을 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.(DL)
- 자바 표준이고, 기능이 단순하므로 단위 테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워진다.
- Provider는 딱 필요한 DL정도의 기능만 제공한다.
- get 메서드 하나로 기능이 매우 단순하고, 별도의 라이브러리가 필요하다
- 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용할 수 있다.
프로토타입 빈 사용시기
- 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다.
- 싱글톤 빈으로 대부분의 문제를 해결할 수 있기 때문에 프로토타입 빈을 직접적으로 사용하는 일은 드물다.
'Spring' 카테고리의 다른 글
[Spring] 빈 생명주기 콜백 (0) | 2023.02.14 |
---|---|
[Spring] 웹 스코프와 프록시 객체 (0) | 2023.02.14 |
[Spring] 빈 스코프, 싱글톤과 프로토타입 (0) | 2023.02.13 |
[Spring] 컴포넌트 스캔 (0) | 2023.02.10 |
[Spring] 싱글톤과 @Configuration (0) | 2023.02.08 |