요청할 때마다 새로운 프로토타입 빈을 제공하도록 하는 방법은 다음 몇 가지가 있다.
- 스프링 컨테이너에 요청
- 스프링 컨테이너를 의존해서 요청이 들어올 때마다 getBean()을 호출한다.
- ObjectFactory, ObjectProvider (스프링 기능)
- 지정한 빈을 컨테이너에서 대신 조회해주는 ObjectProvider를 사용한다.
- 이전에는 ObjectFactory가 있었고, 현재는 편의기능이 추가된 ObjectProvider를 사용한다.
- JSR-330 Provider (자바 표준)
- JSR-330 자바 표준 Provider를 사용한다.
- 스프링 말고 다른 환경에서도 사용할 수 있다는 특징이 있다.
하나씩 살펴보자.
전체 테스트에서 사용되는 PrototypeBean 클래스
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
this.count++;
}
public int getCount() {
return this.count;
}
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init, " + this);
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
스프링 컨테이너에 요청
가장 단순한 방법이다. ApplicationContext를 의존해 주입받고, 필요할 때마다 getBean()을 호출한다.
이런 방식을 의존관계를 직접 찾는다는 의미로 DL (Dependency Lookup, 의존관계 조회, 탐색)이라 한다.
ClientBean 클래스
@Scope("singleton")
static class ClientBean2 {
private final ApplicationContext applicationContext;
@Autowired
public ClientBean2(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public int logic() {
PrototypeBean prototypeBean = applicationContext.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
테스트코드
@Test
public void singletonClientUsePrototype2() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean2.class, PrototypeBean.class);
ClientBean2 clientBean1 = ac.getBean(ClientBean2.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean2 clientBean2 = ac.getBean(ClientBean2.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(1);
}
빈이 필요할 때마다 컨테이너에 직접 요청하므로 각각 다른 프로토타입 빈을 받아 사용할 수 있다.
문제는 해결했지만, 이렇게 작성한 코드는 스프링 컨테이너에 종속적이고 단위 테스트가 어려워진다.
ObjectFactory, ObjectProvider
스프링에 제공하는 ObjectProvider나 ObjectFactory를 사용한다.
ObjectProvider는 지정한 빈을 컨테이너에서 대신 찾아주는, 즉 DL 기능을 제공한다.
기존 ObjectFactory를 상속받아 편의기능을 추가한 것이 ObjectProvider이다.
ObjectProvider를 의존관계로 주입받아 사용한다.
// ObjectProvider를 이용한 프로토타입 빈 조회
@Scope("singleton")
static class ClientBean3 {
private final ObjectProvider<PrototypeBean> prototypeBeans;
@Autowired
public ClientBean3(ObjectProvider<PrototypeBean> prototypeBeans) {
this.prototypeBeans = prototypeBeans;
}
public int logic() {
PrototypeBean prototypeBean = prototypeBeans.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
테스트코드
@Test
public void singletonClientUsePrototype3() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean3.class, PrototypeBean.class);
ClientBean3 clientBean1 = ac.getBean(ClientBean3.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean3 clientBean2 = ac.getBean(ClientBean3.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(1);
}
특징
- 스프링에 종속적이지만, 필요한 DL 기능만 편리하게 이용할 수 있다.
JSR-330 Provider
자바 표준인 javax.inject.Provider를 사용하는 방법이다.
gradle에 JSR-330 라이브러리를 추가한 뒤, Provider<T>를 의존한다.
Provider를 이용한 ClientBean
// 자바 표준 Provider를 이용한 프로토타입 빈 조회
@Scope("singleton")
static class ClientBean4 {
private final Provider<PrototypeBean> prototypeBeans;
@Autowired
public ClientBean4(Provider<PrototypeBean> prototypeBeans) {
this.prototypeBeans = prototypeBeans;
}
public int logic() {
PrototypeBean prototypeBean = prototypeBeans.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
테스트코드
@Test
public void singletonClientUsePrototype4() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean4.class, PrototypeBean.class);
ClientBean4 clientBean1 = ac.getBean(ClientBean4.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean4 clientBean2 = ac.getBean(ClientBean4.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(1);
}
Provider 객체의 get() 메서드를 호출하면 내부에서 스프링 컨테이너를 통해 해당 빈을 조회한다.
특징
- 별도의 라이브러리가 필요하다.
- 자바 표준이기 때문에 스프링 종속적이지 않다.
- get() 메서드 하나로 기능이 단순하다.
+) ObjectProvider(스프링 제공)와 JSR-330 Provider 중 무엇을 써야 하는가?
드물게 스프링 외의 컨테이너에서 동작하도록 작성해야 하는 경우라면 JSR-330을 사용하고,
특별히 다른 컨테이너를 사용할 일이 없다면 ObjectProvider를 사용하는 편이 좋다. (편의기능이 더 많기 때문)
'[inflearn] 스프링 핵심 원리 - 기본편 > 섹션 9 - 빈 스코프' 카테고리의 다른 글
9-6. 웹 스코프 (0) | 2024.07.28 |
---|---|
9-4. 프로토타입과 싱글톤 빈을 함께 사용할 때 주의할 점 (0) | 2024.07.28 |
9-3. 싱글톤 스코프 (0) | 2024.07.27 |
9-2. 프로토타입 스코프 (0) | 2024.07.27 |
9-1. 빈 스코프 (0) | 2024.07.20 |