빈 스코프

Yerim·2021년 9월 13일
0

Spring

목록 보기
9/9
post-thumbnail

✔ Inflearn 강의 수강 내용 정리글입니다!


💡 빈 스코프란?

스프링 빈이 존재할 수 있는 범위

  • 싱글톤 : 스프링 컨테이너와 시작부터 종료까지 유지되는 스코프
  • 프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지 관여하고 더 이상 관리 ❌, 범위가 매우 짧은 빈
  • 웹 관련 스코프
    • request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
    • session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
    • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

빈 스코프는 다음과 같은 방식으로 지정 가능하다. 위는 컴포넌트 스캔 자동 등록, 아래는 수동 등록이다.

@Scope("prototype")
@Component
public class HelloBean {}
@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
 return new HelloBean();
}

💡 프로토타입 스코프

프로토타입 스코프를 스프링 컨테이너에서 조회하면 같은 인스턴스를 반환하는 싱글톤 스코프 빈과 달리 항상 새로운 인스턴스를 생성하여 반환한다.

스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계를 주입하고, 초기화까지만 처리!
클라이언트에 빈을 반환한 후에 스프링 컨테이너는 프로토타입 스코프 빈을 관리하지 않는다.
@PreDestroy같은 종료 메서드는 호출되지 않는다.

프로토타입 빈의 특징

  • 스프링 컨테이너에 요청(조회)할 때마다 새로 생성
  • 스프링 컨테이너는 프로토타입 빈의 생성, 의존관계 주입, 초기화까지만 관여
  • 종료 메서드 호출 ❌
  • 프로토타입 빈은 이를 조회한 클라이언트에게 관리 책임이 있다. 종료 메서드 호출도 클라이언트가 직접 해야한다!

💡 프로토타입 스코프 - 싱글톤 빈과 함께 사용할 때


ClientBean은 싱글톤 빈이므로 스프링 컨테이너 생성 시점에 함께 생성되고 PtoyotypeBean을 주입받는다.
ClientBean은 싱글톤 빈이므로 서로 다른 클라이언트 A, B가 요청할 때마다 항상 같은 ClientBean이 반환된다.
ClientBean이 가지고 있는 PrototypeBean은 이미 과거제 주입이 끝난 빈!

싱글톤 빈은 생성 시점에만 의존관계를 주입받기 때문에 서로 다른 클라이언트가 ClientBean을 요청한다고 해도 요청할 때마다 프로토타입 빈이 생성되는 것이 아니다.

➡ 프로토타입 빈을 주입 시점에만 새로 생성하는 것이 아니라, 사용할 때마다 새로 생성해서 사용하고자 한다면?


💡 Provider로 문제 해결

싱글톤 빈과 프로토타입 빈을 함께 사용할 때, 사용할 때마다 새로운 프로토타입 빈을 생성하고싶다!

스프링 컨테이너에 요청

스프링 컨테이너게 직접 프로토타입 빈을 요청하여 새로운 프로토타입 빈을 반환받는 것도 방법이다.

public int logic() {
 	PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
 	prototypeBean.addCount();
 	int count = prototypeBean.getCount();
 	return count;
 }
  • ac.getBean()을 통해 항상 새로운 프로토타입 빈이 생성되고 반환된다.
  • 의존관계를 외부에서 주입받는 것이 아니라 직접 필요한 의존관계를 찾는 것을 DL(Dependency Lookup)이라고 한다!
  • 스프링 애플리케이션 컨텍스트 전체를 주입받게 되면, 스프링 컨테이너에 종속적인 코드가 되고 단위 테스트도 어려워진다.

ObjectFactory, ObjectProvider

ObjectProvider : 지정한 빈을 컨테이너에서 대신 찾아주는 DL 서비스 제공

public int logic() {
 	PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
 	prototypeBean.addCount();
 	int count = prototypeBean.getCount();
 	return count;
}
  • prototypeBeanProvider.getObject()을 통해 항상 새로운 프로토타입 빈 생성
  • ObjectProvider : 필요한 DL 기능만 제공!

특징

ObjectFactory : 기능 단순, 별도 라이브러리 필요 ❌, 스프링에 의존
ObjectProvider : ObjectFactory 상속, 옵션, 스트림 처리 등 편의 기능이 많고, 별도 라이브러리 필요 ❌, 스프링에 의존


💡 웹 스코프

  • 웹 환경에서만 동작
  • 프로토타입과 다르게 스프링이 해당 스코프의 종료 시점까지 관리 ➡ 종료 메서드 호출!

웹 스코프 종류

  • request : HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리
  • session : HTTP session과 동일안 생명주기를 가지는 스코프
  • application : 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프
  • websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프

💡 request 스코프

동시에 여러 HTTP 요청이 오면 어떤 요청이 남긴 로그인지 구별하지 어렵다 ➡ 이때 사용하기 좋은 것이 request 스코프!

@Component
@Scope(value = "request")
public class MyLogger {
 	private String uuid;
 	private String requestURL;
 	
    	public void setRequestURL(String requestURL) {
 		this.requestURL = requestURL;
 	}
    
 	public void log(String message) {
 
 	}
    
 	@PostConstruct
    	public void init() {

 	}
    
      	@PreDestroy
       	public void close() {

       }
}
  • 해당 빈은 HTTP 요청 당 하나씩 생성되고 HTTP 요청이 끝나는 시점에 소멸

Controller와 Service를 설계한 후 애플리케이션 실행했을 때 시점에 다음과 같은 오류 발생

스프링 애플리케이션을 실행하는 시점에 싱글톤 빈은 생성해서 주입이 가능하지만, request 스코프 빈은 실제 고객의 요청이 와야 생성되므로 아직 생성되지 않는다.


💡 스코프와 Provider

Provider를 통해 문제 해결 가능!

private final ObjectProvider<MyLogger> myLoggerProvider;

MyLogger myLogger = myLoggerProvider.getObject();
myLogger.setRequestURL(requestURL);
  • ObjectProvider.getObject()를 호출하는 시점까지 request scope빈의 생성을 지연

💡 스코프와 프록시

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
}

핵심은 proxyMode = ScopedProxyMode.TARGET_CLASS

  • MyLogger의 가짜 프록시 클래스를 만들어두고 HTTP request와 상관없이 가짜 프록시 클래스를 다른 빈에 주입해둘 수 있다.
private final MyLogger myLogger;

myLogger.setRequestURL(requestURL);

웹 스코프와 프록시 동작 원리

myLogger = class hello.core.common.MyLogger$$EnhancerBySpringCGLIB$$b68b726d

  • CGLIB(바이트코드를 조작하는 라이브러리)라는 라이브러리로 내 클래스를 상속받은 가짜 프록시 객체를 만들어서 주입
  • 가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직을 가지고 있다.
    진짜 MyLogger를 찾는 방법을 알고있다.

정리

  • CGLIB라는 라이브러리로 내 클래스를 상속받은 가짜 프록시 객체를 만들어 주입한다.
  • 가짜 프록시 객체는 실제 요청이 오면 내부에서 실제 빈을 요청하는 위임로직을 가지고 있다.
  • 클라이언트는 프록시 객체를 통해 싱글톤 빈을 사용하듯이 request scope를 편리하게 사용할 수 있다.

    📍 진짜 객체 조회를 꼭 필요한 시점까지 지연처리!


[출처 - Inflearn : 스프링 핵심 원리 - 기본편]
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

profile
Backend-Developer

0개의 댓글