빈 스코프
- 스코프는 빈이 존재할 수 있는 범위를 뜻함
- 스프링은 다음과 같은 스코프를 지원
- 싱글톤 : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하는 매우 짧은 범위의 스코프
- 웹 관련 스코프 : request, session, apllication
프로토타입 스코프
- 프로토타입 스코프의 빈을 스프링 컨에티너에 요청
- 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 의존관계 주입
- 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환
- 이후 스프링 컨테이너에 같은 요청이 오면 항상 새로운 프로토타입 빈을 생성해서 반환
Test
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);
//prototypeBean1.destroy();
//prototypeBean2.destroy();
ac.close();
}
@Scope("prototype")
static class PrototypeBean{
@PostConstruct
public void init(){
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy(){
System.out.println("PrototypeBean.destroy");
}
}
}
- 프로토타입 빈을 2번 조회했으므로 완전히 다른 스프링 빈이 생성되고, 초기화도 2번 실행
- 프로토타입 빈은 스프링 컨테이너가 생성과 의존관계 주입, 초기화 까지만 관여하므로 @PreDestroy같은 종료 메서드를 실행하지 않음(프로토타입 빈을 관리할 책임은 프로토타입 빈을 받은 클라이언트에 있음)
싱글톤과 프로토타입 동시 사용시 문제점
- clientBean은 싱글톤이므로 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생
- 주입 시점에 스프링 컨테이너에 프로토타입 빈을 요창
- 스프링 컨테이너는 프로토타입 빈을 생성한 후 clientBean에 반환(프로토타입 빈의 count 필드 값은 0)
- clientBean은 PrototypeBean을 내부 필드에 보관(참조값을 보관)
- 클라이언트A는 clientBean을 스프링 컨테이너에 요청하여 받음(싱글톤이므로 항상 같은 clientBean 반환)
- 클라이언트A가 clientBean.logic() 호출
- clientBean은 PrototypeBean의 addCount()를 호출해서 PrototypeBean의 count를 증가하여 count값이 1이 됨
- 클라이언트B는 clientBean을 스프링 컨테이너에 요청하여 받음(싱글톤이므로 항상 같은 clientBean 반환)
- 클라이언트B가 clientBean.logic() 호출
- clientBean이 내부에 가지고 있는 PrototypeBean은 이미 과거에 주입이 끝난 빈
- 주입 시점에 스프링 컨테이너에 요청하여 프로토타입 빈이 새로 생성된 것이지 사용 시 마다 새로 생성되는 것은 아님
- clientBean은 PrototypeBean의 addCount()를 호출해서 PrototypeBean의 count를 증가하여 count값이 2가 됨
- 싱글톤 빈은 생성시점에만 의존관계 주입을 받기 때문에 프로토타입 빈이 새로 생성되기는 하지만 싱글톤 빈과 함께 계속 유지되는 문제 발생
싱글톤과 프로토타입 동시 사용시 문제해결
1. ObjectProvider
- 지정된 빈을 컨테이너에 대신 찾아주는 DL 서비스를 제공
- 과거에는 ObjectFactory를 사용하였지만, 여기에 편의 기능을 추가한 것이 ObjectProvider
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(1);
}
@Scope("singleton")
static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
@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");
}
}
}
- getObject()를 통해 항상 새로운 프로토타입 빈이 생성되는 것을 확인
2. JSR-330 Provider
- 자바 표준을 사용하는 방법으로 스프링이 아닌 다른 컨테이너에서도 사용 가능
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("singleton")
static class ClientBean {
@Autowired
private Provider<PrototypeBean> prototypeBeanProvider;
public int logic(){
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
@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");
}
}
}
- get()을 통해 항상 새로운 프로토타입 빈이 생성되는 것을 확인
'Spring > Basic' 카테고리의 다른 글
빈 생명주기 콜백 (1) | 2024.09.01 |
---|---|
의존관계 자동 주입 (0) | 2024.08.29 |
@ComponentScan (0) | 2024.08.21 |
@Configuration (0) | 2024.08.20 |
Singleton (0) | 2024.08.20 |