빈 스코프
업데이트:
빈 스코프
스프링 빈은 기본적으로 싱글톤으로 생성되기 때문에 컨테이너의 시작과 함께 생성되어 종료될때까지 유지된다.
스코프는 번역 그대로 빈이 존재할 수 있는 범위를 뜻한다.
스프링의 스코프 지원
- 싱글톤 : 기본 스코프로 컨테이너 시작과 종료까지 유지되는 가장 넓은 범위의 스코프다.
- 프로토타입 : 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프다.
- 웹 관련 스코프
- request : 웹 요청이 들어오고 나갈때까지 유지된다.
- sessopm : 웹 세션이 생성되고 종료될때까지 유지된다.
- application : 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프다.
자동 등록
@Scope("prototype")
@Component
public class HelloBean{}
수동 등록
@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
return new PrototypeBean;
}
프로토타입 스코프
싱글톤 스코프 빈은 항상 같은 인스턴스를 반환하는 반면에 프로토타입 스코프는 항상 새로운 인스턴스를 생성해서 반환한다.
- 클라이언트에 요청이 왔을때 빈을 생성한다.
- 빈 생성 후 의존관계를 주입한다.
- 프로토타입 빈을 클라이언트에 반환한다.
- 이후 같은 요청이 와도 1번부터 다시 진행한다.
핵심은 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입하고, 초기화까지만 처리한다.
관리할 책임은 호출한 클라이언트에 있으며 @PreDestory 같은 종료 메서드는 호출되지 않는다.
public class PrototypeTest {
@Test
void prototypeBeanFind(){
//직접 등록 컴포넌트 스캔을 자동으로 해줌
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
System.out.println("find prototypeZBean 1");
PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
System.out.println("find prototypeZBean 2");
PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
System.out.println("singletonBean1 = " + prototypeBean1);
System.out.println("singletonBean2 = " + prototypeBean2);
// 서로 다른 객체로 확인
Assertions.assertThat(prototypeBean1).isNotSameAs(prototypeBean2);
//@PreDestroy 호출되지 않아 직접 호출 필요
prototypeBean1.destroy()
ac.close();
}
@Scope("prototype")
static class PrototypeBean{
@PostConstruct
public void init() {
System.out.println("singleton init");
}
//호출 되지 않음
@PreDestroy
public void destroy() {
System.out.println("singleton destory");
}
}
}
싱글톤과 함께 사용시 문제점
- 싱글톤 빈에서 프로토토 타입 빈을 요청해서 프로토 타입빈을 가진 상태를 가정
- 클라이언트에서 싱글톤 빈을 요청해서 빈을 반환받는다.
- 클라이언트에서 싱글톤 빈이 주입 받은 프로토타입 빈의 기능을 호출한다.
- 싱긑톤 빈이 가지고 있는 포로토 타입 빈의 기능을 반환한다.
- 또 다른 클라이언트에서 위 과정을 똑같이 진행하면 싱글톤 빈은 프로토 타입 빈을 처음 호출한 시점에 주입이 끝난 상태라 같은 요청이 오면 기존에 있던 빈의 기능을 반환시킨다.
프로토 타입 빈을 사용하는 이유 자체가 호출할때마다 새로운 객체를 반환 받기 위해서 이다.
싱글톤과 프로토 타입 함께 사용하는 방법
Dipendency Lookup(DL)
- 싱글톤 빈이 프로토 타입을 사용할 때마다 스프링 컨테이너에서 직접 찾는다..
- 컨텍스트에서 빈을 직접찾는걸 Dipendency Lookup(DL) 의존관계 탐색이라고 한다.
- 직접 찾는 로직이 들어가 스프링에 의존적인 코드가 된다.
@Scope("singleton")
static class ClientBean{
@Autowired
private ApplicationContext ac;
public int logic() {
//직접 찾음
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
ObjectFactory, ObjectProvider
- ObjectProvider는 DL 서비스를 제공해줘서 빌요한 빈을 찾아준다.
- ObjectFactory는 getObject() 하나만 있으며, ObjectProvider가 ObjectFactory를 상속 받는다.
- 기능이 단순하여 단위테스트를 만들거나 mock 코드를 만들기 쉬워진다.
- 스프링 기능으로 스프링에 의존해서 사용
@Autowired
private ObjectProvider<PrototypeBean> prototypeProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeProvider.getObject()
prototypeBean.addCount();
return prototypeBean.getCount();
}
JSR-330 Provider
- javax.inject.Provider 자바 표준기술로 gradle에 기능을 추가(라이브러리 추가)해줘야 한다.
- provider.get()을 통해 항상 새로운 프로토타입 빈을 찾아서 반환해준다.(DL)
- 기능이 단순해 mock 코드를 만들기가 쉽다.
프로토 타입빈은 매번 사용할때 의존관계 주입이 완료된 새로운 객체가 필요할떄 사용한다. 실제 업무에서는 싱글톤 빈으로 대부분 해결이 되기 때문에 직접 사용하는 경우는 매우 드물다.
실무에서는 SR-330 Provider, ObjectProvider중 하나를 사용한다면 코드를 스프링이 아닌 다른 곳에서 사용한다면 SR-330 Provide를 사용하고 그렇지 않다면 ObjectProvider를 사용하면 된다.
웹 스코프
웹 환경에서만 동작을 하고 스프링이 해당 스코프의 종료시점까지 관리된다.
종류
- request : HTTP 요청 하나가 들어오가 나갈때까지 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.
- session : HTTP 세션과 동일한 생명주기를 가진다.
- application : 서블릿 컨텍스트와 동일한 생명주기를 가진다.
- websocket : 웹 소켓과 동일한 생명주기를 가진다.
request
- 웹 기반이기 때문에 spring-boot-starter-web 라이브러리가 필요하다
- 웹 라이브러리가 있는 경우 낸장 톰켓 서버를 활용햇거 웹 서버와 스프링을 함께 실행한다.
- 스프링 부트는 웹 라이브러리가 있으면 AnnotationConfigServletWebServerApplicationContext를 기반으로 구동된다.
- UUID를 사용해서 HTTP 요청을 구분할수 있다.
- {UUID}{requestURL}{mesage}
- requestURL은 생성되는 시점을 알 수 없어 외부에서 setter로 입력 받는다.
@Component
@Scope(value = "request")
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL){
this.requestURL = requestURL;
}
public void log(String message){
System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message );
}
@PostConstruct
public void init() {
// 중복되는 uuid 생성해줌, PostConstruct를 통해 자동 생성해줌
String uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "] request scope bean create : " + this);
}
@PreDestroy
public void close(){
//웹 요청 반환시 PreDestroy를 통해 자동 종료
System.out.println("[" + uuid + "] request scope bean close : " + this);
}
}
프록시
- proxyMode를 추가해서 Proovider 없이 요청 지연을 할 수 있다
- 가짜 프록시 객체를 만들어 스프링에 등록하고 의존성을 주입한다.
- 위임 로직이 들어가 있어 요청이 왔을떄 실제 객체를 만들어 스프링에 등록하고 의존성을 주입한다.
- 클래스 : proxyMode = ScpedProxyMode.TARGET_CLASS
- 인터페이스 : proxyMode = ScopedProxyMode.INTERFACES
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {}
댓글남기기