스코프: 빈이 존재할 수 있는 범위
싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입(초기화 메서드까지)
까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
클라이언트에게 빈을 반환하고, 스프링 컨테이너는 이후에 빈을 관리하지 않음(이후에는 클라이언트가 관리)
- 컨테이너에 요청할 때마다 새로 생성
- 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드도 실행
- 종료 메서드가 호출되지 않음 (클라이언트가 직접 해야함)
프로토 타입 스코프, 싱글톤 빈 함께 사용 시 문제점
public class SingletonWithPrototypeTest1 {
@Test
void prototypeFind(){ // 스프링 컨테이너에 프로토타입 빈 직접 요청
// 요청 할 때마다 새롤운 빈을 생성하기 때문에 조회한 두개의 프로토타입 빈의 count는 각각 1이다.
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean2.class);
PrototypeBean2 prototypeBean = ac.getBean(PrototypeBean2.class);
prototypeBean.addCount();
assertThat(prototypeBean.getCount()).isEqualTo(1);
PrototypeBean2 prototypeBean2 = ac.getBean(PrototypeBean2.class);
prototypeBean2.addCount();
assertThat(prototypeBean2.getCount()).isEqualTo(1);
}
@Test
void singletonClientUsePrototype(){
// 싱글톤 빈은 생성 시점에만 의존관계 주입을 받기 때문에 처음 생성된 프로토타입이 계속 유지된다.
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean2.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{ // 싱글톤에서 프로토타입 빈 사용
// ClientBean 은 싱글톤이므로 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생한다.
private final PrototypeBean2 prototypeBean2; // 의존관계 주입시 프로토타입빈을 요청하고 스프링 컨테이너가 프로토타입 빈을 생성해 반환한다. //
// 생성 시점에 주입하고, 싱글톤이므로 항상 같은 clientBea이 반환되고 prototypeBean도 계속 같은걸 사용
public ClientBean(PrototypeBean2 prototypeBean2) { // 프로토타입 빈 달라고 컨테이너에 요청 -> 만들어 줌
this.prototypeBean2 = prototypeBean2;
}
public int logic(){
prototypeBean2.addCount();
return prototypeBean2.getCount();
}
}
@Scope("prototype")
static class PrototypeBean2{ // 프로토타입 빈
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");
}
}
}
지정한 프로토타입 빈을 컨테이너에서 대신 찾아주는 DL 기능을 사용해 이 문제를 해결한다.
Dependency Lookup: 의존관계 조회(탐색)
ObjectProvider
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider; // 직접 등록하지 않아도
// 스프링에서 자동으로 만들어 등록
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject(); // 항상
// 새로운 프로토타입 빈 생성하게 해줌
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
ObjectProvider의 getObject() 를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.
기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워진다.
웹 관련 스코프
request: 웹 요청이 들어오고 나갈때 까지 유지되는 스코프
- 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성,관리
session: 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
- HTTP session 과 동일한 생명주기 가짐
application: 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프
참고) 스프링 핵심 원리 기본편
'BACK > SPRING' 카테고리의 다른 글
[Spring] @ExceptionHandler를 사용해 예외 처리하기 (1) | 2024.04.18 |
---|---|
[Spring] 스프링 MVC1- 웹 애플리케이션 이해(WAS, 서블릿, 쓰레드) (0) | 2024.03.04 |
[Spring] 스프링 핵심 원리 - 빈 생명주기 콜백 (0) | 2024.03.04 |
[Spring] 스프링 핵심 원리 - 의존관계 자동 주입 (0) | 2024.01.31 |
[Spring] 스프링 핵심 원리 - 컴포넌트 스캔 (0) | 2024.01.31 |