9. 빈 스코프
빈 스코프
- 싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지
- 프로토타입: 빈의 생성과 의존관계 주입까지만 관여하고 관리안함. 필요할때마다 매번 생성 (싱글톤과 반대), destroy도 책임 안짐
- 웹 관련 스코프
- request: 웹 요청이 들어오고 나갈때까지 유지
- session: 웹 세션이 생성되고 종료될때까지 유지 (여러 request에 유지)
- application: 웹의 서블릿 컨텍스와 같은 범위
- websocket: 웹소켓과 동일한 같은 범위
사용법 @Scope
@Scope("prototype")
@Component
public class HelloBean {}
- class 또는 메소드 위에 @Scope를 달아서 지정하면된다.
싱글톤 타입과 프로토타입이 같이 사용될때 문제가 생긴다.
- 싱글톤에서 프로토타입 주입시 싱글톤은 처음에 주입받은 프로토타입을 계속 사용하기때문에 새로 초기화가 안된다.
- 프로토타입 설계 자체를 사용할때마다 초기화 한걸 기대했으면 오류가 날 수 도있다.
- 극단적인 해결방법 중 하나는 applicationContext를 받아와서 매번 사용할때마다 context에서 꺼내오면된다.
- Provider를 사용해서 해결할 수 있다!
- Dependency Lookup 의존관계를 자동으로 주입이아니라, 필요한것을 찾아 사용하는 것.
ObjectFactory, ObjectProvider
- ObjectProvider, ObjectFactory 사용법. 대신 context에서 찾아서 생성해준다.
@RequiredArgsConstructor
static class ClientBean {
private final ObjectProvider<PrototypeBean> prototypeBeanObjectProvider;
// private final ObjectFactory<PrototypeBean> prototypeBeanObjectProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanObjectProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
- ObjectProvider, ObjectFactory 둘다 똑같이 사용 가능
- ObjectProvider가 ObjectFactory를 상속받았고, 추가 편의 기능들도 제공
- 내부에서 스프링 컨테이너에서 해당 빈을 그때그때 찾아 반환
- 스프링에 의존적
JSR-330 Provider (자바 표준으로 정의된 Provider)
- build.gradle에 추가해줘야한다. `implementation ‘javax.inject:javax.inject:1’
import javax.inject.Provider; // * 꼭 javax.inject 라이브러리 사용해야한다.
@RequiredArgsConstructor
static class ClientBean {
private final Provider<PrototypeBean> prototypeBeanObjectProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanObjectProvider.get();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
- 별도의 라이브러리가 필요하다.
- 자바 표준 라이브러리서 다른 컨테이너에서도 사용가능.
- DL은 순환참조에 사용 가능
근데 그러면 spring이 만든 ObjectProvider랑 자바 표준 Provider 중에는 뭘 사용해야되지??
- 스프링은 대세가 스프링에서만든 코드 사용하는것… ㅋㅋㅋㅋ 자바 표준이 아직 기능이 너무 불편해서 사람들이 사용을 잘않나다.
request scope 빈을 사용해보자!
@Component
@Scope(value = "request")
public class MyLogger { ... }
-----
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
private final MyLogger myLogger;
public void logic(String id) {
// MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + id);
}
}
-
MyLogger를 생성자 주입통해서 받을라면 현재 스코프에 생성된 빈이 없다고 실행자체가 안된다.

-
해결 방법으로는 ObjectProvider를 통해 사용하는 쪽에서 매번 getObject를 해서 사용해야한다.
Provider 보다 더 좋은 방법이 있었다! proxyMode를 사용하자!
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger { ... }
- proxyMode를 ScopedProxyMode.TARGET_CLASS로 설정해두면, 프록시 패턴을 통해 CGLIB 라이브러리를 통해 바이트 코드를 조작해서 원래 객체, 수정된 프록시 객체를 생성한다.
- 객체를 사용하는 곳에서 context에 지연 조회를 한다.