1. 빈 스코프란
스코프 : 빈이 존재할 수 있는 범위를 뜻한다.
- 싱글톤
- 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입
- 빈 요청을 하면, 빈 생성, 의존관계 주입, 초기화 메소드까지 호출하고 빈을 제공한다.
- 종료메소드는 호출되지 않는다.
- 프로토타입 빈의 주입까지만 관여하고 더는 관리하지 않는 짤은 범위의 스코프다.
- @Scope("prototype")
- 빈 요청을 하면, 빈 생성, 의존관계 주입, 초기화 메소드까지 호출하고 빈을 제공한다.
웹 관련 스코프
- request : 웹 요청이 들어오고 나갈때 까지 유지되는 스코프이다.
- session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프이다.
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프이다.
2. 프로토타입 스코프
싱글톤은 항상 같은 인스턴스의 빈을 반환 <-> 프로토타입은 항상 새로운 인스턴스를 생성해서 반환
클라이언트의 프로토타입 빈 요청 과정
- 프로토타입 스코프의 빈을 스프링 컨테이너에 요청한다.
- 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 의존관계를 주입하고, 초기화 메서드를 수행한다.
- 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환한다.
- 스프링 컨테이너는 여기까지만 관여한다.
- 따라서 @PreDestroy 같은 스프링 컨테이너가 관여하는 종료메서드는 호출되지 않는다.
- 직접 수작업으로 종료 메서드를 호출해야한다.
- 같은 요청이 오면, 항상 새로운 프로토타입 빈을 생성해서 반환한다.
3. 프로토타입과 싱글톤을 함께 사용할 시 문제점
싱글톤 빈이 프로토타입 빈을 주입받아 사용하는 경우
- 프로토타입 빈을 요청한다.
- 스프링 컨테이너는 프로토타입 빈을 생성해서 반환한다.
- ClientA가 로직(프로토타입 빈의 내부 필드값을 +1하는 로직) 을 호출
- 0->1 받음
- ClientB가 로직(프로토타입 빈의 내부 필드값을 +1하는 로직) 을 호출
- 1->2 받음
@Test
void singletonClientUsePrototype() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
ClientBean clientBean1 = ac.getBean(ClientBean.class);
int count1 = clientBean1.logic();
assertEquals(count1, 1);
ClientBean clientBean2 = ac.getBean(ClientBean.class);
int count2 = clientBean2.logic();
assertEquals(count2, 2);
}
@RequiredArgsConstructor
static class ClientBean {
private final PrototypeBean prototypeBean;
public int logic() {
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
}
문제점
- 싱글톤 빈은 스프링 컨테이너 생성시점에 한번만 생성된다.
- 싱글톤 빈이 처음 생성될 때, 싱글톤 빈에 주입할 프로토타입 빈이 아직 존재하지 않아, 스프링 컨테이너에 요청하게 된다.
- 이후 싱글톤 빈은 처음에 주입받은 프로토타입 빈을 계속 참조하고 있다.
- 다른 클라이언트가 이후에 요청을 해도, 위의 테스트처럼 프로토타입 내부 필드값은 초기화가 되어있지않고, 계속 변한상태로 유지된다.
4. 프로토타입과 싱글톤을 함께 사용할 시 문제점을 Provider로 해결
// 싱글톤 빈에서 현재, 프로토타입 빈을 주입받기를 원하여 직접 의존관계를 찾고있다.
- 이러한 과정을 DL(의존관계 조회(탐색) Dependency LookUp) 이라 한다.
위의 ClientBean을 아래와같이 변경한다.
- ObjectProvider<PrototypeBean> prototypeBeanProvider 를 정의한 뒤
- prototypeBeanProvider.getObject() 를 통해 프로토타입 빈을 가져온다. (Dependency LookUp)
@RequiredArgsConstructor
static class ClientBean {
private final ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
Provider<T>
- 스프링에 의존적인 ObjectProvider이 아닌 javax.inject에서 제공하는 provider을 사용할 수 있다.
- Provider.get() 을 사용한다.
implementation 'javax.inject:javax.inject:1'
@RequiredArgsConstructor
static class ClientBean {
private final Provider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
'강의 정리 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
6. 빈 생명주기 콜백 (0) | 2020.12.23 |
---|---|
4. 컴포넌트 스캔 (0) | 2020.12.17 |
3. 싱글톤 컨테이너 (0) | 2020.12.15 |
2. 스프링 컨테이너와 스프링 빈 (0) | 2020.12.13 |
1. 객체지향 설계와 스프링 (0) | 2020.12.12 |