싱글톤, 프로토타입 스코프 빈 함께 사용
인프런 스프링 핵심 원리 - 기본편 수강 중
함께 사용할 때 문제점
프로토타입 빈 직접 요청
- 클라이언트A가 스프링 컨테이너에 프로토타입 빈 요청
- 스프링 컨테이너는 프로토타입 빈을 새로 생성해 반환, 필드값 0
- 클라이언트A가 조회한 프로토타입 빈에
addCount()
호출해 필드값 + 1 - 프로토타입 빈의 필드값은 1
- 클라이언트B가 스프링 컨테이너에 프로토타입 빈 요청
- 스프링 컨테이너는 프로토타입 빈을 새로 생성해 반환, 필드값 0
- 클라이언트가 조회한 프로토타입 빈에
addCount()
호출해 필드값 + 1 - 역시 프로토타입 빈의 필드값은 1
코드 테스트
public class SingletonWithPrototypeTest1 {
@Test
void prototypeFind(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
prototypeBean1.addCount();
assertThat(prototypeBean1.getCount()).isEqualTo(1);
PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
prototypeBean2.addCount();
assertThat(prototypeBean2.getCount()).isEqualTo(1);
}
@Scope("prototype")
static class PrototypeBean{
private int count = 0;
public void addCount(){
count++;
}
public int getCount() {
return count;
}
@PostConstruct
public void init(){
System.out.println("PrototypeBean.init = " + this);
}
@PreDestroy
public void destroy(){
System.out.println("PrototypeBean.destory");
}
}
}
두 프로토타입 빈의 필드값 모두 1
싱글톤 빈에서 프로토타입 빈 사용
clientBean
이라는 싱글톤 빈에서 프로토타입 빈을 주입받아 사용clientBean
은 싱글톤이기 때문에 스프링 컨테이너 생성 시점에 함께 생성 및 의존관계 주입 발생
clientBean
은 의존관계 자동 주입, 주이 시점에 스프링 컨테이너에 프로토타입 빈 요청- 스프링 컨테이너가 프로토타입 빈 생성 후
clientBean
에 반환, 프로토타입 빈 필드값 0 clientBean
은 프로토타입 빈을 내부 필드에 보관
- 클라이언트A가 스프링 컨테이너에
clientBean
요청- 싱글톤이기 때문에 항상 같은
clientBean
반환
- 싱글톤이기 때문에 항상 같은
- 클라이언트A가
clientBean.logic()
호출 clientBean
은prototypeBean
의addCount()
를 호출해서 프로토타입 빈의 필드값 + 1
- 클라이언트A가 스프링 컨테이너에
clientBean
요청- 싱글톤이기 때문에 항상 같은
clientBean
반환 clientBean
이 내부에 가지고 있는 프로토타입 빈은 이미 주입이 끝난 빈- 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 생성되기 때문에 새로운 생성이 되는 것은 아님
- 싱글톤이기 때문에 항상 같은
- 클라이언트B가
clientBean.logic()
호출 clientBean
은 내부prototypeBean
의addCount()
를 호출해서 프로토타입 빈의 필드값 + 1 되어 2가 됨
코드 테스트
public class SingletonWithPrototypeTest1 {
...
@Test
void singletonClientUsePrototype(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
ClientBean clientBean1 = ac.getBean(ClientBean.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean clientBean2 = ac.getBean(ClientBean.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(2);
}
@Scope("singleton")
static class ClientBean{
private final PrototypeBean prototypeBean; //생성시점에 주입
@Autowired
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic(){
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Scope("prototype")
static class PrototypeBean{
private int count = 0;
public void addCount(){
count++;
}
...
}
}
프로토타입 빈의 필드값이 증가함
프로토타입 빈의 사용 목적은 항상 새로운 빈을 생성하는 것이기 때문에 의도한 것과 다른 결과가 나온다
문제점 해결 - Provider
스프링 컨테이너에 요청
@Autowired
private ApplicationContext ac;
public int logic() {
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
ac.getBean()
을 통해 항상 새로운 프로토타입이 생성- 의존관계를 외부에서 주입받는 DI가 아니라 직접 필요한 의존관계를 찾는 DL(Dependency Lookup) 의존관계 조회(탐색)
- 스프링 컨테이너에 종속적
- 단위테스트에 어려움
ObjectFactory, ObjectProvider
static class ClientBean{
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
prototypeBeanProvider.getObject()
를 통해 항상 새로운 프로토타입 빈이 생성됨getObject()
호출시 스픵 컨테이너를 통해 해당 빈을 찾아 반환 (DL)- 단위테스트나 mock 코드 만들기 쉬움
ObjectProvider
는ObjectFactory
를 상속받아 편의 기능이 추가된 것
JSR-330 Provider
JSR-330 자바 표준을 사용하기 위해 라이브러리를 gradle에 추가해야 한다
javax.inject:javax.inject:1
테스트 코드
@Scope("singleton")
static class ClientBean{
@Autowired
private Provider<PrototypeBean> prototypeBeanProvider;
public int logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
- 선언 해주고 메서드명만 바꾸어주면 동일 동작
get()
메서드 하나로 기능 매우 단순- 자바 표준이므로 스프링이 아닌 다른 컨테이너에서 사용 가능
[참조] 인프런 스프링 핵심 원리 - 기본편 - 링크
끝!