1. 빈 스코프(Scope)란?
→ 빈이 존재할 수 있는 범위
스프링이 지원하는 스코프
- 싱글톤(Singleton) : 기본 스코프, 스프링 컨테이너 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입(Prototype) : 스프링 컨테이너가 빈의 생성과 의존관계 주입 까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
- 웹 관련 스코프 : request, session, application
2. 프로토 타입 스코프
- 항상 새로운 인스턴스를 생성해서 반환
- 빈을 생성, 의존관계 주입, 초기화 까지만 처리
→ @PreDestory 같은 종료 메서드가 호출되지 않는다.
public class PrototypeTest {
@Test
void prototypeBeanFind() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
System.out.println("find prototypeBean1");
PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
System.out.println("find prototypeBean2");
PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
System.out.println("prototypeBean1 = " + prototypeBean1);
System.out.println("prototypeBean2 = " + prototypeBean2);
assertThat(prototypeBean1).isNotSameAs(prototypeBean2);
ac.close(); /* 스프링 컨테이너가 종료되지만, 각 빈의 인스턴스는 사라지지 않기때문에
수동 destroy() 메서드를 호출해야한다. */
//소멸자 호출 코드
//prototypeBean1.destroy();
//prototypeBean2.destroy();
}
@Scope("prototype")
static class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
}
- @Scope(”prototype”) 으로 프로토타입 스코프 지정
- 소멸자 수행을 워하면 직접 호출 해야한다.
→prototypeBean1.destroy();
코드 작성
스프링 컨테이너가 prototype을 관리하는 범위
- 스프링 빈 생성
- 의존관계 주입(DI)
- 초기화 작업
—> 초기화 작업후 관리하지 않기 때문에 소멸자 실행이 되지 않음
프로토타입 빈의 특징
- 스프링 컨테이너에 요청할 때마다 새로 생성됨
- 프로토타입 빈의 생성 및 주입, 초기화 까지만 관여
- 종료 메서드가 호출되지 않음 → 사용자가 직접 종료 메서드 호출 해야함
3. 싱글톤 안에 프로토 타입이 있는경우
- Singleton Bean 내부의 Prototype Bean이 있는 Test에서 오류 발생
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); //오류발생!!
/* Singleton내부에 있는 Prototype Bean은 최초 의존관계 주입 시점에 딱 1번 수행됨
그 이후에 호출 할때는 Prototype Bean이 생성되지 않고 처음 주입된 PrototypeBean객체가
호출되는 것 이므로 값이 1이 아닌 2인 것이다. */
}
//Prototype Bean을 가지는 Singleton Bean 생성
static class ClientBean {
private final PrototypeBean prototypeBean;
@Autowired
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic() {
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
//프로토 타입 Bean 객체
@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.destroy");
}
}
}
💡 오류 발생 이유?
- Singleton Bean이 생성되는 최초의 시점에 Prototype Bean이 생성된다. 그 후 호출되는 모든 객체는 똑같은 Prototype Bean을 참조(Singleton)해서 계속 누적해서 count 값이 더해지므로 오류가 발생하는 것이다.
오류 해결 → Provider의 사용
- Dependency Lookup(DL) : 의존 관계 조회(탐색), 직접 필요한 의존관계를 찾는 것
- 싱글톤 내부에 프로토타입 빈이 있을 때 호출할 때마다 프로토타입 빈이 새로운 값을 할당 받는 방법 : 호출할 때마다 bean을 찾아서 만들어 준다.
→ 원하는 프로토 타입 빈만 가져오기 위해 DL을 사용
Provider의 종류
1. ObjectiveProvider (구 ObjectFactory)
ObejctProvider
의getObject()
를 호출시, 스프링 컨테이너를 통해 해당 빈을 찾아서 반환(DL)
ObjectProvider의 특징
- ObjectFactory를 상속 받아서 옵션, 스트림 처리등 편의기능
- 별도 라이브러리 필요 없음
- 스프링에 의존
@Scope("singleton") //prototype Bean을 가지는 Singleton Bean 생성
static class ClientBean {
@Autowired
//Object Provider를 이용해서 DL 구현
private Provider<PrototypeBean> prototypeBeanProvider;
//getObject 메서드로 새로운 prototype Bean을 할당
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
2. JSR-330 Provider
- 자바 표준에서 제공하는 Provider이다.
- 별도의 라이브러리를 추가해야 한다. :
javax.inject:javax.inject:1
를 gradle에 추가 provider.get()
메서드를 통해서 새로운 프로토타입 빈 생성
JSR-330 Provider의 특징
- get()
메서드 하나로 기능이 매우 단순
- 별도의 라이브러리가 필요함
- 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용 가능
@Scope("singleton") //prototype Bean을 가지는 Singleton Bean 생성
static class ClientBean {
@Autowired
//JSR-330 Provider를 이용해서 DL 구현
private Provider<PrototypeBean> prototypeBeanProvider;
//get 메서드로 새로운 prototype Bean을 할당
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
요약
- 기본적으로 스프링에 내장된 기능인 ObjectProvider를 사용하자!
- 다른 컨테이너에서도 사용해야 한다면 JSR-330Provider를 사용할 것
반응형