빈 스코프란?
스코프: 빈이 존재할 수 있는 범위
다양한 스코프가 있음
싱글톤: 기본 스코프. 시작부터 종료까지 유지되는 가장 넓은 범위.
프로토타입: 빈의 생성과 의존관계 주입까지만 관여하고 그 이후는 관리하지 않는 스코프.
웹 관련 스코프
request: 웹 요청이 들어오고 나갈때 까지 유지
session: 세션이 생성되고 종료될 때까지 유지
application: 웹의 서블릿 컨텍스트와 같은 범위
프로토타입 스코프
싱글톤 스코프 빈 -> 항상 같은 인스턴스의 스프링 빈을 반환함.
프로토타입 스코프 빈 -> 요청 시점에 빈을 생성하고 의존 관계 주입함 -> 스프링 컨테이너는 빈을 반환했다가 같은 요청이 왔을 때 새로운 빈을 다시 생성해서 반환 함.
=> 생성된 프로토타입 빈을 관리하지 않음
=> 관리 책임이 클라이언트에게로 가게 되고, @PreDestroy 같은 종료 메서드가 호출되지 않음.
@Scope("prototype")
static class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
// 결과
find prototypeBean1
PrototypeBean.init
find prototypeBean2
PrototypeBean.init
bean1 = hello.demo.scope.PrototypeTest$PrototypeBean@306851c7
bean2 = hello.demo.scope.PrototypeTest$PrototypeBean@12bcd0c0
프로토타입 빈은 스프링에서 관리하지 않기 때문에 종료 메서드(destroy())가 ac.close()로 컨테이너가 종료됬을 때도 호출되지 않는 것을 확인 할 수 있음.
=> 이런 경우 직접 prototypeBean1.destory() 메소드 호출을 통해 클라이언트에서 직접 호출해줘야 한다.
싱글톤 빈과 함께 사용시 문제점
@Scope("singleton")
static class ClientBean {
private final PrototypeBean prototypeBean; // 생성시점에 주입됨
@Autowired
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic() {
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
생성 시점에서 PrototypeBean을 주입 받게 되면서 원하던 로직과는 다른 로직이 실행 되게 될 것임.
ClientBean1, ClientBean2가 생겨서 새로운 클래스에 각각 주입 받게 되면 새로운 Bean을 주인 받게 될 것임.
싱글톤 빈과 함께 사용시 Provider로 문제 해결
가장 간단한 방법 - 싱글톤 빈이 프로토타입 빈을 요청 시 마다 스프링 컨테이너에 요청하기 -> ApplicationContext를 주입 받아서 사용해버리기 (무식)
=> Dependency Lookup (DL) 의존관계 조회 관계
=> 스프링 컨테이너에 종속적인 코드. 단위 테스트가 어려워 짐.
ObjectFactory, ObjectProvider
@Scope("singleton")
static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanObjectProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanObjectProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
ObjectFactory (인터페이스) - 기능이 단순, 별도 라이브러리 필요 없음. 스프링 의존.
ObjectProvider (상속 받음) + 관련 기능 추가 제공 - 스프링 컨테이너 조회를 도와주는 대리자. 스프링에 의존.
--> 패키지 자체가 springframework..
JSR-330 Provider
자바 표준 사용 -> 단위 테스트를 만들거나 mock 코드를 만들기 편리하고 스프링 컨테이너 이외에도 사용 가능.
gradle에 라이브러리 추가 필요.
프로토 타입빈은 언제 사용할까?
매번 사용시마다 의존관계 주입이 완료된 새로운 객체가 필요할 때 사용 필요 --> 웹 애플리케이션에서 개발해보면 싱글톤 빈으로 대부분 해결 가능함. 직접적으로 사용하는 일은 매우 드묾.
스프링 - 자바 표준에서 제공하는 기능이 겹칠 때: 스프링이 제공하는 기능을 사용하는 것이 편할 수 있음. (현재는..)
웹 스코프
싱글톤은 스프링 컨테이너의 시작과 끝까지 함께하는 매우 긴 스코프.
웹 환경에서만 동작함
종류
request: HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프. 요청마다/ 별도의 빈 인스턴스 생성 됨.
session: HTTP Session과 동일
application: 서블릿 컨텍스트와 동일한 생명 주기
websocket: 웹 소켓과 동일한 생명주기
request 스코프 예제 만들기
웹 환경에서만 동작함으로 라이브러리 추가 필요함
spring-boot-starter-web (Tomcat 실행됨 - 웹 서버)
웹 관련 추가 설정과 환경들이 필요함으로 AnnotationConfigServletWebServerApplicationContext 기반으로 애플리케이션 구현 됨
@Scope(value = "request"): HTTP 요청 당 하나씩 생성, 끝나는 시점에서 소멸 됨.
스프링에서 관리하는 빈이기 때문에 생성(@PostContruct), 제거(@PreDestroy) 메소드 호출 됨.
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final MyLogger myLogger;
...
// 생성자 주입을 통해서 자동으로 들어감 - 하지만 MyLogger는 scope가 request 임으로 오류 발생
}
MyLogger가 스코프가 request이기 때문에 애초에 스프링이 띄우는 단계에서는 존재하지 않음으로 오류 발생.
사실 로그 남기는 동일한 부분 같은 경우는 스프링 인터셉터, 서블릿 필터와 같은 부분을 이용해서 공통처리 가능함.
스코프와 Provider
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + id);
}
}
ObjectProvider.getObject()를 호출하는 시점까지 빈의 생성 지연 가능
Controller -> Service까지의 로직이 하나의 HTTP request이기 때문에 빈의 스코프가 동일한 것이 유지된다.
스코프와 프록시
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
}
클래스: TARGET_CLASS
인터페이스: INTERFACES
가짜 프록시 클래스를 만들어 두고 클래스를 다른 빈에 미리 주입 할 수 있음.
클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입 함. - CGLIB 라이브러리
가짜 프록시 객체는 요청 왔을 때 내부에서 진짜 빈을 요청하는 로직이 들어있음.
애노테이션의 설정 변경만으로도 프록시 객체 대체 가능.
최소화해서 사용하자!
유지보수가 어려운 코드가 되어버림.
'Spring' 카테고리의 다른 글
스프링 핵심 원리 기본편 - 챕터 8. 빈 생명주기 콜백 (0) | 2023.03.26 |
---|---|
스프링 핵심 원리 기본편 - 챕터 7. 의존관계 자동 주입 (0) | 2023.03.26 |
스프링 핵심 원리 기본편 - 챕터 6. 컴포넌트 스캔 (0) | 2023.03.19 |
스프링 핵심 원리 기본편 - 챕터 5. 싱글톤 컨테이너 (0) | 2023.03.19 |
스프링 핵심 원리 기본편 - 챕터 4 (0) | 2023.03.12 |
댓글