빈 스코프

KOO HEESEUNG·2021년 10월 22일
0
post-thumbnail

빈 스코프란?

빈이 존재할 수 있는 범위

스프링은 다음과 같은 다양한 스코프를 지원한다.

  1. 싱글톤 : 기본 스코프. 스프링 컨테이너 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  2. 프로토타입 : 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
  3. 웹 관련 스코프
    • request : 웹 요청이 들어오고 나갈때까지 유지되는 스코프
    • session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프
    • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
@Scope("proptotype")
@Component
public class HelloBean {}

프로토타입 스코프

프로토타입 빈이란

스프링 컨테이너에 요청할 때마다 항상 새로운 인스턴스를 생성해서 반환한다.

스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리한다. 그래서 @PreDestroy 같은 종료 메서드가 호출되지 않는다.

따라서 프로토타입 빈은 이를 조회한 클라이언트가 관리해야 한다. 종료 메서드 호출도 클라이언트가 직접 해주어야 한다.

언제 사용할까?

매번 사용할 때마다 의존관계 주입이 완료된 새로운 객체가 필요할 때.

프로토타입 빈을 직접적으로 사용하는 일은 드물다.

싱글톤 빈과 함께 사용시 문제점

@Scope("prototype")
static class PrototypeBean {
  private int count = 0;

  public void addCount() {
    count++;
  }

  public int getCount() {
    return count;
  }

  @PostConstruct
  public void init() {
    System.out.println("PrototypeBean.init " + this);
  }

  @PreDestroy
  public void destroy() {
    System.out.println("PrototypeBean.destroy");
  }
}

@Scope("singleton")
static class ClientBean {
  private final PrototypeBean prototypeBean;

  public ClientBean(PrototypeBean prototypeBean) {
    this.prototypeBean = prototypeBean;
  }

  public int logic() {
    prototypeBean.addCount();
    return prototypeBean.getCount();
  }
}

싱글톤 빈이 프로토타입 빈을 사용하는 경우, 싱글톤 빈은 생성 시점에만 의존관계를 주입받기 때문에 프로토타입 빈이 새로 생성되기는 하지만, 싱글톤 빈과 함께 계속 유지된다.

해결방법

  1. 싱글톤 빈이 프로토타입을 사용할 때마다 스프링 컨테이너에 새로 요청
@Scope("singleton")
static class ClientBean {

  @Autowired
  ApplicationContext ac;

  public int logic() {
    PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
    prototypeBean.addCount();
    return prototypeBean.getCount();
  }
}
  • ac.getBean() 을 통해 항상 새로운 프로토타입 빈이 생성됨
  • 의존관계를 외부에서 주입(DI)받는 게 아니라 직접 필요한 의존관계를 찾음. 이를 Dependency Lookup(DL, 의존관계 조회) 이라 함.
  • 스프링 컨테이너에 종속적인 코드가 되고, 단위 테스트도 어려운 단점.
  1. ObjectFactory, ObjectProvider
@Scope("singleton")
static class ClientBean {

  @Autowired
  private ObjectProvider<PrototypeBean> prototypeBeanProvider;

  public int logic() {
    PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
    prototypeBean.addCount();
    return prototypeBean.getCount();
  }
}
  • prototypeBeanProvider.getObject(); 를 통해 항상 새로운 프로토타입 빈이 생성됨.
  • 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.(DL)
  • 스프링에 의존적
  1. JSR-330 Provider
    javax.inject.Provider 라는 JSR-330 자바 표준을 사용하는 방법. 라이브러리를 gradle에 추가해야 한다.
@Scope("singleton")
static class ClientBean {

  @Autowired
  private Provider<PrototypeBean> provider;


  public int logic() {
    PrototypeBean prototypeBean = provider.get();
    prototypeBean.addCount();
    return prototypeBean.getCount();
  }
}
  • provider.get 을 호출하면 내부에서 스프링 컨테이너를 통해 해당 빈을 찾아 반환한다(DL)
  • 자바 표준이고, 기능이 단순하므로 단위테스트나 mock 코드 만들기가 쉽다.
  • 지금 딱 필요한 DL 정도의 기능만 제공한다.
  • 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용 가능.

웹 스코프

웹 환경에서만 동작.

스프링이 해당 스코프의 종료 시점까지 관리한다.

종류

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

request 스코프

[HTTP request 요청당 각각 할당되는 request 스코프]

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

스프링 애플리케이션을 실행하는 시점에 싱글톤 빈은 생성해서 주입이 가능하지만, request 스코프 빈은 실제 고객의 요청이 와야 생성할 수 있으므로, 오류가 발생한다.

해결방법 1. Provider

ObjectProvider.getObject() 를 호출하는 시점까지 request 스코프 빈의 생성을 지연할 수 있다.

ObjectProvider.getObject()controllerservice 에서 각각 한번씩 따로 호출해도 같은 HTTP 요청이면 같은 스프링 빈이 반환된다.

해결방법 2. Proxy

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

@ScopeproxyMode 를 추가한다.

  • 적용 대상이 클래스면 TARGET_CLASS, 인터페이스면 INTERFACES 선택

해당 어노테이션이 붙은 클래스의 가짜 프록시 클래스를 만들어서 주입

가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.

특징

  • 클라이언트가 싱글톤 빈 사용하듯 편리하게 request scope를 사용 가능
  • 진짜 객체 조회를 필요한 시점까지 지연처리한다.

주의사항

  • 싱글톤과 비슷해 보이지만, 다르게 동작하기 때문에 주의해서 사용해야 한다.
  • 특별한 scope는 꼭 필요한 곳에만 최소화하여 사용할 것. 무분별한 사용은 유지보수를 어렵게 한다.

0개의 댓글