본 내용은 인프런의 김영한 님의 강의 "스프링 핵심 원리 - 기본편" 내용을 바탕으로 정리한 내용입니다.
빈스코프란?
빈 스코프란 스프링 프레임워크에서 빈 객체의 생명주기와 인스턴스의 유효 범위를 나타내는 개념이다.
Singleton(싱글톤) 스코프
- 기본 스코프로, 애플리케이션 내에서 해당 빈의 인스턴스가 오직 하나만 생성된다. 스프링 컨테이너의 시작부터 종료까지 유지되는 가장 넓은 범위의 스코프이다.
- 데이터베이스 연결 풀, 로깅 객체 등에 사용된다.
Prototype(프로토타입) 스코프
- 매번 빈을 요청할 때마다 새로운 인스턴스가 생성된다.
- 스프링 컨테이너는 빈의 생성과 주입, 초기화까지만 처리하기에 매우 짧은 범위의 스코프이다.
- 종료 메서드를 호출하지 않는다.
- 프로토타입 빈은 클라이언트가 직접 관리해야 한다.
- 웹 요청 시 생성되는 컨트롤러, 요청마다 다른 사용자 정보를 담는 객체 등에 사용된다.
예시)
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class PrototypeBeanScopeTest {
private final ApplicationContext context;
@Autowired
public PrototypeBeanScopeTest(ApplicationContext context) {
this.context = context;
}
@Component
@Scope("prototype")
public static class PrototypeBean {
// 프로토타입 빈 클래스
}
@Test
public void testPrototypeBeanScope() {
// 프로토타입 빈을 두 번 생성하여 서로 다른 인스턴스인지 확인
PrototypeBean bean1 = context.getBean(PrototypeBean.class);
PrototypeBean bean2 = context.getBean(PrototypeBean.class);
assertThat(bean1).isNotSameAs(bean2); // 두 빈이 다름
}
}
프로토타입빈을 싱글톤 빈과 같이 사용하는 방법
스프링은 일반적으로 싱글톤 빈을 사용하는데 싱글톤 빈이 프로토타입 빈을 사용하면 프로토타입 빈이 새로 생성은 되지만 이미 생성된 싱글톤 빈에 의해 스프링 컨테이너에 해당 빈이 계속 유지된다.
1. ObjectProvider
스프링 4.3부터 도입된 Provider를 확장한 인터페이스로, 프로토타입 빈을 지연된 방식으로 주입받아 매번 새로운 인스턴스를 가져올 수 있도록 도와주는 클래스이다. ObjectProvider를 사용하면 싱글톤 빈이 프로토타입 빈을 필요로 할 때마다 항상 새로운 인스턴스를 가져올 수 있습니다.
예시)
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class SingletonBean {
private final ObjectProvider<PrototypeBean> prototypeBeanProvider;
@Autowired
public SingletonBean(ObjectProvider<PrototypeBean> prototypeBeanProvider) {
this.prototypeBeanProvider = prototypeBeanProvider;
}
public void doSomethingWithPrototypeBean() {
// 프로토타입 빈을 필요로 할 때마다 항상 새로운 인스턴스를 가져옴
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
// 프로토타입 빈을 사용하여 로직 수행
}
}
@Component
@Scope("prototype")
public class PrototypeBean {
// 프로토타입 빈 클래스
}
2. JSR-330 Provider
스프링과는 별개로 자바 표준인 자바 서블릿 API와 JavaEE에서 제공하는 기술로 자바에서 의존성 주입을 위한 일관된 방법을 제공하기 위한 목적으로 만들어졌다. jakarta.inject:jakarta.inject-api:2.0.1 라이브러리를 gradle에 추가해야 한다.
예시)
import javax.inject.Inject;
import javax.inject.Provider;
@Component
@Scope("singleton")
public class SingletonBean {
private final Provider<PrototypeBean> prototypeBeanProvider;
@Inject
public SingletonBean(Provider<PrototypeBean> prototypeBeanProvider) {
this.prototypeBeanProvider = prototypeBeanProvider;
}
public void doSomethingWithPrototypeBean() {
// 프로토타입 빈을 필요로 할 때마다 항상 새로운 인스턴스를 가져옴
PrototypeBean prototypeBean = prototypeBeanProvider.get();
// 프로토타입 빈을 사용하여 로직 수행
}
}
@Component
@Scope("prototype")
public class PrototypeBean {
// 프로토타입 빈 클래스
}
※ 참고 : ObjectProvider는 DL을 위한 편의 기능을 많이 제 공해주고 스프링 외에 별도의 의존관계 추가가 필요 없기 때문에 편리하다. 만약 코드를 스프링이 아닌 다른 컨테이너에서도 사용할 수 있어야 한다면 JSR-330 Provider를 사용해야한 다.
웹 스코프(Web Scope)란?
- 웹 애플리케이션에서 빈(Bean)의 생명 주기와 유효 범위를 정의하는 스코프이다.
- 웹 환경에서 각각의 HTTP 요청마다 빈의 인스턴스가 생성되고 소멸되는 방식을 제어하는데 사용된다.
- 웹 스코프는 스프링이 해당 스코프의 종료까지 관리한다. 따라서 종료 메서드가 호출된다.
1. Request(요청) 스코프
- 웹 애플리케이션에서만 사용 가능한 스코프로, HTTP 요청 당 하나의 빈 인스턴스를 생성한다.
- 웹 요청이 들어오고 나갈때까지 유지된다.
- 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되므로 요청 간에 빈의 상태가 공유되지 않는다.
- 웹 애플리케이션의 HTTP 요청 당 유지되는 객체, 요청마다 다른 로그 정보를 담는 객체 등에 사용된다.
2. Session(세션) 스코프
- 웹 애플리케이션에서만 사용 가능한 스코프로, HTTP 세션 당 하나의 빈 인스턴스를 생성한다.
- 웹 세션이 생성되고 종료될 때까지 유지된다.
- 한 세션 내에서 빈의 인스턴스가 공유되므로 세션에 속한 여러 요청에서 동일한 빈을 사용할 수 있다.
- 웹 애플리케이션의 HTTP 세션 당 유지되는 객체, 사용자의 로그인 정보를 담는 객체 등에 사용된다.
3. Application(애플리케이션) 스코프
- 웹 애플리케이션이 시작될 때 생성되고, 웹 애플리케이션이 종료될 때까지(웹의 서블릿 컨텍스트와 같은 범위) 유지되는 스코프이다.
- 웹 애플리케이션 전체에서 단 하나의 빈 인스턴스만 생성된다.
- 모든 요청 및 세션에서 이 빈 인스턴스를 공유하여 사용한다.
- 주로 전역 설정 정보나 공통적으로 사용되는 객체 등에 사용된다.
4. WebSocket(웹소켓) 스코프
- 웹 소켓(WebSocket) 연결마다 빈(Bean)의 인스턴스를 생성하고 해당 웹 소켓 연결 내에서만 공유하는 스코프이다.
- 웹 소켓 연결마다 독립적인 빈 인스턴스를 가질 수 있으며, 웹 소켓 연결의 생명 주기에 따라 빈의 유효 범위를 제어할 수 있다.
- 웹 소켓과 동일한 생명주기를 가지는 스코프이다.
예시) "localhost:8080/main" URL로 HTTP 요청시 request 스코프를 할당하는데 Controller와 Service는 모두 같은 HTTP 요청이므로 같은 스프링 빈이 반환된다.
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("request")
public class RequestScopedBean {
private String requestData;
public void setRequestData(String requestData) {
this.requestData = requestData;
}
public void printRequestData(String call) {
System.out.println("Call: " + call + " / " + "Request Data: " + requestData);
}
}
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
import lombok.RequiredArgsConstructor;
@Controller
@RequiredArgsConstructor
public class SpringController {
private final ObjectProvider<RequestScopedBean> requestScopedBeanProvider;
private final SpringService springService;
@RequestMapping(value = "main")
public String getMain(HttpServletRequest request) {
String url = request.getRequestURL().toString();
RequestScopedBean requestScopedBean = requestScopedBeanProvider.getObject();
requestScopedBean.setRequestData(url);
// 출력 : Call: Controller / Request Data: http://localhost:8080/main
requestScopedBean.printRequestData("Controller");
springService.processRequestData();
return "main";
}
}
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class SpringService {
private final ObjectProvider<RequestScopedBean> requestScopedBeanProvider;
public void processRequestData(String data) {
RequestScopedBean requestScopedBean = requestScopedBeanProvider.getObject();
// 출력 : Call: Service / Request Data: http://localhost:8080/main
requestScopedBean.printRequestData("Service");
}
}
스코프와 프록시
- ScopedProxyMode.TARGET_CLASS 설정은 프록시(가짜 객체)를 클래스 기반으로 생성하는 방법이다.
- 프록시는 실제 빈에 대한 래퍼 역할을 수행하며, 해당 빈이 필요로 하는 시점에 실제 빈을 초기화하여 사용한다.
- CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.
- 요청이 들어오면 내부에서 진짜 빈을 요청하는 위임로직이 들어가 있다.
예시)
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
// ...
}
'Spring' 카테고리의 다른 글
[스프링] 쓰레드 로컬 (0) | 2024.05.04 |
---|---|
[스프링] 로그 추적기 (0) | 2024.05.04 |
[스프링] 스프링 빈의 생명주기 콜백 (0) | 2023.07.23 |
[스프링] 스프링 빈의 자동 주입과 수동 주입 (0) | 2023.07.22 |
[스프링] 커스텀 어노테이션(Custom Annotation) (0) | 2023.07.22 |