빈 스코프
- 빈 스코프 : 빈이 존재할 수 있는 범위를 의미한다.
- @Scope("스코프 종류")로 빈 스코프를 지정할 수 있다.
- 스프링 빈은 기본적으로 싱글톤 스코프로 생성되기 때문에 스프링 컨테이너의 시작과 함께 생성되었다가 컨테이너 종료될 때 소멸한다.
- 스프링 빈에서 지원하는 스코프 종류
- 싱글톤 : 기본 스코프로 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프이다.
- 프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입, 초기화까지만 관여하고 더이상 관리하지 않는 매우 짧은 범위의 스코프이다.
- 스프링 빈에서 지원하는 웹 관련 스코프 종류
- request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프이다.
- session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프이다.
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프이다.
프로토타입 빈
- 프로토타입 스코프
- 싱글톤과 달리 요청할 때마다 스프링 컨테이너는 새로운 인스턴스를 생성해서 반환해준다.
- 프로토타입 스코프 빈을 요청하는 시점에 빈을 생성하고 의존관계를 주입한다.
- 요청에 빈을 반환해주고 더는 관리하지 않는다.
- 스프링 컨테이너는 프로토타입 빈을 생성, 의존관계 주입, 초기화까지만 처리하고 더는 관리하지 않는다.
- @PreDestroy 같은 종료 메소드가 호출되지 않는다.
- 프로토타입 빈을 관리할 책임은 요청을 한 클라이언트에게 있다. (종료 메소드 호출도 클라이언트가 해야한다)
- 싱글톤에서 프로토타입 빈 사용하는 경우
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(PrototypeBean.class, ClientBean.class);
ClientBean clientBean1 = ac.getBean(ClientBean.class);
int count1 = clientBean1.logic();
ClientBean clientBean2 = ac.getBean(ClientBean.class);
int count2 = clientBean2.logic();
===================================================
@Scope("singleton")
@Configuration
public class ClientBean {
private final PrototypeBean prototypeBean; //생성시점에 주입
@Autowired
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic() {
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Scope("prototype")
@Configuration
public class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
}
- 예제에서 프로토타입(PrototypeBean) 빈을 싱글톤(ClientBean) 빈 내부에서 사용한다.
- ClientBean 내부에 PrototypeBean 객체가 있다.
- ClientBean은 싱글톤으로 컨테이너 생성과 동시에 빈이 생성되며 의존관계도 주입 받는다.
- PrototypeBean 객체는 생성자 주입을 통해 ClientBean 생성 시점에 의존관계를 주입받는다.
- 프로토타입 빈은 호출될 때마다 빈이 생성되지만 싱글톤인 ClientBean에서 PrototypeBean 객체는 생성자 주입 시점에 딱 한 번만 호출되고 이후에는 같은 객체를 반환해준다.
- 이 예제에서 프로토타입 빈은 사용할 때마다 객체가 생성되지 못하고 싱글톤 방식으로 한 객체를 계속 반환해주는 문제가 발생한다.
- 싱글톤 빈과 프로토타입 빈을 함께 사용 시 문제점 해결방법
- 의존관계를 외부에서 주입받지 않고 getBean()을 통해 직접 조회(DL)함으로 항상 새로운 프로토타입 빈을 생성하게 할 수 있다.
- 스프링 컨테이너에 종속족인 코드가 되고 단위 테스트가 어려워진다.
- ObjectFactory<> / ObjectProvider<>를 사용한다.
- getObject() 호출 시점에 컨테이너를 통해 해당 빈을 찾아서 반환(DL)함으로 항상 새로운 프로토타입 빈이 생성된다.
- 딱 필요한 정도의 DL 기능만을 제공한다.
- 스프링에 의존적이다.
- JSR-330 Provider를 사용한다.
- 자바 표준을 사용하는 방법으로 스프링에 의존적이지 않다.
- 별도의 라이브러리를 추가해줘야한다.
- 의존관계를 외부에서 주입받지 않고 getBean()을 통해 직접 조회(DL)함으로 항상 새로운 프로토타입 빈을 생성하게 할 수 있다.
- 의존관계 조회(탐색) DL(Dependency Lookup) : 의존관계를 외부에서 주입받는 것이 아닌 직접 필요한 의존관계를 찾는 것
//ObjectProvider / ObjectFactory 방식
//나머지 코드는 이전과 동일
@Scope("singleton")
@Configuration
public class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
==============================================
//JSR-330 Provider 방식
//나머지 코드는 이전과 동일
@Scope("singleton")
@Configuration
public class ClientBean {
@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
- ObjectProvider<> / ObjectFactory<> 방식
- getObject() 호출 시점에 프로토타입 빈을 찾는다 -> 프로토타입 빈 생성 => logic() 메소드 호출 시마다 새로운 프로토타입 빈이 생성된다.
- ObjectProvider 대신 ObjectFactory를 사용할 수 있다.
- ObjectProvider가 ObjectFactory를 상속받고 있다.
- ObjectProvider는 프로토타입 빈 전용이 아니라 스프링 컨테이너에서 조회할 때 대리로 조회해주는 역할
- 별도의 라이브러리가 필요없고 스프링에 의존적이다.
- JSR-330 Provider<> 방식
- get() 호출 시마다 항상 새로운 프로토타입 빈이 생성된다.
- ObjectProvider 방식과 유사하다.
- 자바 표준 방식으로 스프링에 의존적이지 않기에 다른 컨테이너에서도 사용할 수 있다.
- 별도의 라이브러리가 필요하다.
- ObjectProvider, JSR330 Provider를 사용하는 경우
- 프로토타입 빈을 싱글톤에서 사용하기 위해서
- 의존관계가 순환 참조가 발생하는 경우
- A가 B를 의존하고 B가 A를 의존하는 경우 Provider를 사용해서 DL을 할 수 있다.
- DL이 필요한 경우 언제든지 사용할 수 있다.
- 프로토타입 빈을 사용하는 경우
- 매번 사용할 때마다 의존관계 주입이 완료된 새로운 객체가 필요할 때 사용한다.
- 대부분 싱글톤으로 해결되기 때문에 거의 사용하는 일은 드물다.
웹 스코프
- 웹 스코프 특징
- 웹 환경에서만 동작한다.
- 프로토타입과 달리 해당 스코프의 종료시점까지 관리한다. (종료 메소드가 호출된다)
- 웹 스코프 종류
- request : HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프로 각 HTTP 요청마다 별도의 빈이 생성, 관리된다.
- session : HTTP Session과 동일한 생명주기를 가지는 스코프
- application : 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프
- websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프
- 웹 라이브러리가 없을 때는 AnnotationConfigApplicationContext 기반으로 애플리케이션을 구동한다.
- 웹 라이브러리가 추가되면 AnnotationConfigServletWebServerApplicationContext를 기반으로 애플리케이션을 구동한다.
- 동시에 여러 HTTP 요청이 들어오면 어떤 요청이 남긴 로그인지 구분하기 힘들다.
- 이때 사용하기 좋은 것이 request 스코프이다.
- @RequestMapping : 특정 uri 요청을 특정 메소드와 매핑하기 위해서 사용하는 어노테이션이다.
- @ResponseBody : view 화면 없이 문자 / 자바 객체 그대로 응답으로 보낼 수 있다.
- request 스코프의 빈을 생성자 주입으로 의존관계를 주입받게 되면 예외가 발생한다.
- request 스코프 빈은 HTTP 에서 요청을 했을 때 빈이 생성되는데 스프링 컨테이너를 시작하는 시점에는 HTTP 요청이 없기 때문에 request 스코프 빈을 찾지 못해서 예외가 발생하게 된다.
- request 스코프 빈의 의존관계를 주입하는 방법
- 생성자 주입을 사용하는 것이 아닌 HTTP 요청이 들어온 시점에 Provider를 사용해 DL로 빈을 찾는 방식이 있다.
- @Scope()의 proxyMode 옵션을 사용하는 방식이 있다.
- 적용대상이 클래스면 @Scope 옵션으로 proxyMode = ScopedProxyMode.TARGET_CLASS 를 추가하고 인터페이스면 ScopedProxyMode.INTERFACES 를 추가한다.
- @Scope의 proxyMode 옵션을 사용하면 처음에는 가짜 프록시 클래스를 빈에 등록시키고 필요한 곳에 의존관계 주입을 시켜놓는다. 이후 HTTP 요청이 들어오면 진짜 클래스를 빈으로 등록한 후 찾아서 Provider와 같이 동작을 한다.
- 즉, proxyMode 옵션을 사용하면 request 스코프 빈도 생성자 주입로 의존관계를 주입받을 수 있다.
- 가짜 프록시 객체는 원본 클래스를 상속 받아서 만들어졌으며 클라이언트 입장에서는 원본인지 아닌지 모르게 동일하게 사용할 수 있다. (다형성)
- 두 가지 해결 방법의 핵심은 진짜 객체 조회를 꼭 필요한 시점까지 지연처리를 한다는 점이다.
- request 스코프 빈은 같은 HTTP 요청이면 Controller, Service, Repository 어디든 같은 스프링 빈이 반환된다.
출처 : [인프런 김영한 스프링 핵심 원리 - 기본편]
스프링 핵심 원리 - 기본편 강의 | 김영한 - 인프런
김영한 | 스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보
www.inflearn.com
'Spring > [인프런 김영한 스프링 핵심 원리 -기본편]' 카테고리의 다른 글
[인프런 김영한 스프링 핵심 원리 - 기본편] 빈 생명주기 콜백 (0) | 2024.08.14 |
---|---|
[인프런 김영한 스프링 핵심 원리 - 기본편] 의존관계 자동 주입 (1) | 2024.08.14 |
[인프런 김영한 스프링 핵심 원리 - 기본편] 컴포넌트 스캔 (0) | 2024.08.14 |
[인프런 김영한 스프링 핵심 원리 - 기본편] 싱글톤 컨테이너 (0) | 2024.08.13 |
[인프런 김영한 스프링 핵심 원리 - 기본편] 스프링 컨테이너와 스프링 빈 (1) | 2024.08.13 |