말 그대로 빈이 존재할 수 있는 범위
🔷 싱글톤
기본 스코프
스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
🔷 프로토타입
프로토타입 빈의 생성과 의존관계까지만 관여
초기화 메서드까지 호출하고 끝, 종료 메서드 호출 ❌
매우 짧은 범위의 스코프
🔷 웹 관련 스코프
🔹 request : 웹 요청이 들어오고 나갈때까지 유지되는 스코프
🔹 session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
🔹 application : 웹의 서플릿 컨텍스트와 같은 범위로 유지되는 스코프
🔷 빈 스코프 지정 방법
🔹 컴포넌트 스캔 자동 등록
@Scope("prototype")
@Component
public class DDeo99{}
🔹 수동 등록
@Scope("prototype")
@Bean
PrototypeBean DDeo99() {
return new DDeo99();
}
프로토타입 스코프를 스프링 컨테이너에 조회
➡️ 항상 새로운 인스턴스를 생성해서 반환
스프링 컨테이너는 프로토타입 빈을 생성, 의존관계 주입, 초기화까지만 처리
스프링 컨테이너는 생성된 프로토타입 빈을 관리하지 않음
➡️ @PreDestory와 같은 종료 메서드 호출 ❌
➡️ 프로토타입 빈은 프로토타입 빈을 조회한 클라이언트가 관리해야함
➡️ 종료 메서드에 대한 호출도 클라이언트가 직접 해야함
싱글톤 스코프의 빈을 조회
➡️ 스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈 반환
싱글톤 빈은 스프링 컨테이너 생성 시점에 초기화 메서드 실행
프로토타입 빈은 스프링 컨테이너에서 빈을 조회할 때 생성, 초기화 메서드 실행
프로토타입 스코프의 빈은 싱글톤 빈과 함께 사용하면 의도한 대로 동작하지 않음
스프링은 일반적으로 싱글톤 빈 사용
➡️ 싱글톤 빈과 프로토타입 빈을 함께 사용하면 싱글톤 빈이 프로토타입 빈을 사용
➡️ 싱글톤 빈은 생성 시점에만 의존관계 주입
➡️ 프로토타입 빈이 새로 생성되기는 하나 싱글톤 빈과 함께 계속 유지되어 문제 발생
➡️ 사용할 때마다 새로 생성해서 사용하는 프로토타입 빈의 목적을 이루지 못함
지정한 프로토타입 빈을 컨테이너에서 대신 찾아주는 정도의 기능만 제공받을 수 있으면 프로토타입 빈과 싱글톤 빈을 함께 사용할 때의 문제를 해결할 수 있음
➡️ ObjectFactory, ObjectProvider
- 의존관계 외부 주입 : DI
➡️ 스프링의 애플리케이션 컨텍스트 전체를 주입받아 스프링 컨테이너에 종속적인 코드가 됨
- 의존관계 조회(탐색): DL(Dependency Lookup)
➡️ 직접 필요한 의존관계를 찾는 것
ObjectFactory에 편의 기능을 추가해서 만듦
스프링 컨테이너를 통해서 대신 조회해주는 대리자
애플리케이션 컨텍스트를 통해서가 아닌 지정한 빈을 스프링 컨테이너에서 대신 찾아주는 DL 서비스를 제공
public void 메서드() {
// getObject() : 스프링 컨테이너에서 프로토타입 빈을 찾아 반환
PrototypeBean prototypeBean = prototypeBeanObjectProvider.getObject();
//...
}
prototypeBeanProvider.getObject()를 통해서 항상 프로토타입 빈 생성
JSR-330 자바 표준
스프링 부트 3.0 미만 : javax.inject:javax.inject:1 라이브러리 추가
스프링 부트 3.0 이상 : jakarta.inject:jakarta.inject-api:2.0.1 라이브러리 추가
<@Autowired
private Provider<PrototypeBena> provider;
public void 메서드() {
PrototypeBean prototypeBean = prototypeBeanObjectProvider.get();
//...
}
매번 사용할 때마다 의존관계 주입이 완료된 새로운 객체가 필요한 경우에 사용
실무에서 웹 애플리케이션 개발할 때, 싱글톤 빈으로 대부분의 문제 해결 가능
➡️ 잘 사용 안함
- ObjectFactory : 기능 단순, 별도의 라이브러리 필요 없음, 스프링에 의존
- ObjectProvider : ObjectFactory 상속, 옵션 스트림 처리 등의 편의 기능 O, 별도의 라이브러리 필요 없음, 스프링에 의존
- JSR-330 Provicer : get() 메서드 하나로 기능이 매우 단순, 별도의 라이브러리 필요, 자바 표준 -> 다른 컨테이너에서도 사용 가능
웹 스코프는 웹 환경에서만 동작 -> web 환경이 동작하도록 라이브러리 추가해야함
프로토타입과 달리 스프링이 해당 스코프의 종료 시점까지 관리 -> 종료 메서드 호출
//web 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-web'
-참고-
웹과 관련된 부분은 컨트롤러까지만 사용
서비스 계층은 웹 기술에 종속되지 않고, 가급적 순수하게 유지하는 것이 유지보수 관점에서 좋음
▪️ request
: HTTP 요청이 들어와서 나갈 때까지 유지, 각각의 HTTp 요청마다 별도의 빈 인스턴스 생성 및관리
클라이언트 A가 요청을 하면 HTTP 요청이 들어와서 나갈 때까지 하나의 A 전용 스프링 빈 사용
클라이언트 B가 요청을 하면 HTTP 요청이 들어와서 나갈 때까지 하나의 B 전용 스프링 빈 사용
▪️ session : HTTP Session과 동일한 생명주기 가짐
▪️ application : ServletContext와 동일한 생명주기 가짐
▪️ websocket : 웹 소켓과 동일한 생명주기 가짐
동시에 여러 HTTP 요청이 오면 어떤 요청이 남긴 로그인지 구분하기 어려움
➡️ request 스코프를 활용
✔️ 다만 스프링 애플리케이션을 실행하는 시점에 request 스코프 빈은 생성되지 않으므로
이 문제를 해결하여 사용해야 함
ObjectProvider를 사용함으로써 ObjectProvider.getObject()를 호출하는 시점까지
request 스코브 빈을 스프링 컨테이너에 요청하는 것을 지연할 수 있음
ObjectProvider.getObject() 호출하는 시점은 HTTP 요청 진행중
➡️ request 스코프 빈 생성 OK
proxyMode = ScopedProxyMode.TARGET_CLASS 옵션 추가
적용 대상이 인터페이스 - INTERFACES
적용 대상이 인터페이스 X - TARGET_CLASS
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class 클래스 {
}
'클래스'의 가짜 프록시 클래스를 만들어두고 HTTP request와 상관 없이 가짜 프록시 클래스를 다른 빈에 미리 주입 가능
➡️ CGLIB 라이브러리가 내 클래스를 상속받은 가짜 프록시 개체를 만들어 주입
➡️ 우리가 등록한 '클래스'가 아닌 '클래스'$$EnhancerBySpringCGLIB 객체가 대신 등록
➡️ ac.getBean()을 사용해서 우리가 등록한 클래스를 조회해도 프록시 객체가 조회됨
➡️ 의존 관계 주입도 이 가짜 프록시 개체가 주입됨
가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직 존재
➡️ 가짜 프록시 객체는 내부에 진짜 '클래스'를 찾는 방법을 알고 있음
➡️ 가짜 프록시 객체는 원본 클래스를 상속받아서 만들어짐
➡️ 이 객체를 사용하는 클라이언트는 원본인지 아닌지도 모르게 동일하게 사용 가능(다형성)
가짜 프록시 개체는 실제 request scope과는 관계 없음, 싱글톤처럼 동작
어노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체 가능(다형성, DI컨테이너 의 강점)
마치 싱글톤 빈을 사용하듯이 편리하게 request scope 사용 가능
(싱글톤을 사용하는 것 같지만 동작 방식은 다름 -> 필요한 곳에만 최소화해서 사용)
✅ Provider든 Proxy든 중요한 점은 진짜 객체 조회를 필요한 시점까지 지연 처리한다는 것
인프런 스프링 핵심 원리 - 기본편 (김영한) 참조