본문 바로가기
강의 정리/스프링 핵심 원리 - 기본편

7. 빈 스코프

by 이석준석이 2020. 12. 24.

1. 빈 스코프란

 

스코프 : 빈이 존재할 수 있는 범위를 뜻한다.

 

  • 싱글톤
    • 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  • 프로토타입
    • 빈 요청을 하면, 빈 생성, 의존관계 주입, 초기화 메소드까지 호출하고 빈을 제공한다.
      • 종료메소드는 호출되지 않는다.
    • 프로토타입 빈의 주입까지만 관여하고 더는 관리하지 않는 짤은 범위의 스코프다.
    • @Scope("prototype")

 

웹 관련 스코프

  • request : 웹 요청이 들어오고 나갈때 까지 유지되는 스코프이다.
  • session : 웹 세션이 생성되고 종료될 때까지 유지되는 스코프이다.
  • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프이다.

2. 프로토타입 스코프

 

싱글톤은 항상 같은 인스턴스의 빈을 반환 <-> 프로토타입은 항상 새로운 인스턴스를 생성해서 반환

 

클라이언트의 프로토타입 빈 요청 과정

  1. 프로토타입 스코프의 빈을 스프링 컨테이너에 요청한다.
  2. 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 의존관계를 주입하고, 초기화 메서드를 수행한다.
  3. 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환한다.
    • 스프링 컨테이너는 여기까지만 관여한다.
    • 따라서 @PreDestroy 같은 스프링 컨테이너가 관여하는 종료메서드는 호출되지 않는다.
      • 직접 수작업으로 종료 메서드를 호출해야한다.
  4. 같은 요청이 오면, 항상 새로운 프로토타입 빈을 생성해서 반환한다.

3. 프로토타입과 싱글톤을 함께 사용할 시 문제점

 

싱글톤 빈이 프로토타입 빈을 주입받아 사용하는 경우

  1. 프로토타입 빈을 요청한다.
  2. 스프링 컨테이너는 프로토타입 빈을 생성해서 반환한다.
  3. ClientA가 로직(프로토타입 빈의 내부 필드값을 +1하는 로직) 을 호출 
    1. 0->1 받음
  4. ClientB가 로직(프로토타입 빈의 내부 필드값을 +1하는 로직) 을 호출
    1. 1->2 받음
@Test
void singletonClientUsePrototype() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);

    ClientBean clientBean1 = ac.getBean(ClientBean.class);
    int count1 = clientBean1.logic();
    assertEquals(count1, 1);

    ClientBean clientBean2 = ac.getBean(ClientBean.class);
    int count2 = clientBean2.logic();
    assertEquals(count2, 2);
}

@RequiredArgsConstructor
static class ClientBean {
    private final PrototypeBean prototypeBean;

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

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

    public void addCount() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

문제점

  • 싱글톤 빈은 스프링 컨테이너 생성시점에 한번만 생성된다.
  • 싱글톤 빈이 처음 생성될 때, 싱글톤 빈에 주입할 프로토타입 빈이 아직 존재하지 않아, 스프링 컨테이너에 요청하게 된다.
  • 이후 싱글톤 빈은 처음에 주입받은 프로토타입 빈을 계속 참조하고 있다.
    • 다른 클라이언트가 이후에 요청을 해도, 위의 테스트처럼 프로토타입 내부 필드값은 초기화가 되어있지않고, 계속 변한상태로 유지된다.

4. 프로토타입과 싱글톤을 함께 사용할 시 문제점을 Provider로 해결

 

// 싱글톤 빈에서 현재, 프로토타입 빈을 주입받기를 원하여 직접 의존관계를 찾고있다.

  • 이러한 과정을 DL(의존관계 조회(탐색) Dependency LookUp) 이라 한다.

 

위의 ClientBean을 아래와같이 변경한다.

  • ObjectProvider<PrototypeBean> prototypeBeanProvider 를 정의한 뒤
  • prototypeBeanProvider.getObject() 를 통해 프로토타입 빈을 가져온다. (Dependency LookUp)
@RequiredArgsConstructor
static class ClientBean {
    private final ObjectProvider<PrototypeBean> prototypeBeanProvider;

    public int logic() {
        PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        prototypeBean.addCount();
        return prototypeBean.getCount();
    }
}

Provider<T>

  • 스프링에 의존적인 ObjectProvider이 아닌 javax.inject에서 제공하는 provider을 사용할 수 있다.
  • Provider.get() 을 사용한다.
implementation 'javax.inject:javax.inject:1'
@RequiredArgsConstructor
static class ClientBean {
    private final Provider<PrototypeBean> prototypeBeanProvider;

    public int logic() {
        PrototypeBean prototypeBean = prototypeBeanProvider.get();
        prototypeBean.addCount();
        return prototypeBean.getCount();
    }
}