빈 스코프

빈 스코프

빈 스코프 : 빈이 존재할 수 있는 범위 #

기본 값 : singleton (애플리케이션 시작 ~ 종료)

즉, 스프링 컨테이너의 시작과 끝 (동일한 범위를 갖는다.)


스코프 종류 #

singleton, prototype, request 는 기억하고 넘어갈 것

  • singleton
  • prototype
    • 빈 생성 / 의존관계 / 초기화하여 반환하고 끝 (관리 X)
    • 매우 짧은 범위의 스코프
  • request (웹 관련 scope)
    • 웹 요청이 들어오고 나갈 때 까지의 스코프
  • session (웹 관련 scope)
    • 웹 세션 생성 ~ 종료 스코프
  • application (웹 관련 scope)
    • 웹 서플릿 컨텍스트와 같은 범위

웹스코프 (라이브러리 필요 : spring-boot-starter-web)

  • 웹 환경에서만 동작
  • 해당 스코프의 종료시점까지 관리 (종료 메서드 호출)

prototype 스코프 #

[요약]

1. PreDestroy 와 같은 종료 콜백 메서드 실행 X
2. 매번 새롭게 Bean 생성


빈을 요청하면 항상 새로운 Bean(인스턴스)를 생성/반환

  • 싱글톤 => 스프링 컨테이너 띄워질 때 생성해놓고 관리
  • 프로토타입 => 조회/요청 시에 생성/반환
  • Bean 생성 -> 반환 -> 초기화 -> 끝 (더 이상 관리하지 않음)

관리의 주체 : Bean 을 받은 클라이언트 (싱글톤의 경우 -> 스프링 컨테이너)

  • 따라서, PreDestory 같은 종료 메서드 실행되지 않음

실무에서 거의 사용되지 않는다고함


싱글톤 빈과 함께 사용할 때 주의할 점

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

@Scope("prototype")
class PrototypeBean {

}
  1. ClientBean 생성 / DI 시점에 PrototypeBean 이 프로토타입 형태로 주입 받음
  2. 이후에도 ClientBean 은 PrototypeBean 을 가지고 있고, 다시 주입받을 일이 없음. (요청할 일이 없음)
  3. 결론 => PrototypeBean 싱글톤 느낌으로 동작하게 됨.
    • prototype 의도한대로 하려면 아래와 같이 사용해야함
// 방법 1 : ApplicationContext 사용 
// - 코드가 너무 지저분해짐
// - 단위 테스트 어렵고, 스프링 컨테이너(ApplicationContext)에 의존
@Scope("singleton")
class ClientBean {

    private final ApplicationContext applicationContext;

    void add() {

        // DL(Dependency LookUp) : 의존관계를 직접 조회/찾음 (<-> DI)
        PrototypeBean prototypeBean = applicationContext.getBean(PrototypeBean.class);
        prototypeBean.addCount();
        ...
    }
}

// 방법 2 : ObjectProvider 사용  (springframework 제공)
// 위에서 DL 역할만 있으면 됨.
@Scope("singleton")
class ClientBean {

    private final ObjectProvider<PrototypeBean> prototypeBeanProvider;

    void add() {
        // DL(Dependency LookUp) : 의존관계를 직접 조회/찾음 (<-> DI)
        PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        prototypeBean.addCount();
        ...
    }
}

// 방법 3 : Provider 사용 (Java 표준, 의존성 추가 : javax.inject:javax.inject:1)
@Scope("singleton")
class ClientBean {

    private final Provider<PrototypeBean> prototypeBeanProvider;

    void add() {
        // DL(Dependency LookUp) : 의존관계를 직접 조회/찾음 (<-> DI)
        PrototypeBean prototypeBean = prototypeBeanProvider.get();
        prototypeBean.addCount();
        ...
    }
}

(주의할 점) ObjectProvider 는

  • prototype 을 위해 만들어진게 아님
  • ApplicationContext LookUp 을 위해 사용하는 것
  • 단, 스프링에 의존

(주의할 점) Provider 는

  • java 표준
  • 의존성 추가



request 스코프 (웹 스코프) #

  • HTTP 요청 당 유지되는 스코프 (즉, HTTP 응답 나갈 때, 종료)
  • 각각의 HTTP 요청 마다 Bean 생성/관리

e.g. 동시에 2 Client 가 요청하면, 각각 다른(다른 HTTP 요청이기이) 빈 생성

(아마 아래 스코프도 동일할 듯) 아래와 같은 코드는 오류 발생

이유 :

  1. MyService 는 웹 애플리케이션이 뜰 때 Bean 으로 등록된다.
  2. 이때, MyLog 를 주입받아야 하는데, MyLog 는 HTTP 요청이 없으니 Bean 으로 관리 X
@Service
class MyService {
    private final MyLog myLog;

    public void logic() {
        ...
    }
}

@Scope("request")
class MyLog {
    ...
}

방법 1 : Provider

DL 은 여러 번 호출 되어도, 같은 생명주기 내에서는 동일한 Bean 보장

Controller, Service 에서 myLogProvider.getObject() 호출 -> 동일한 Bean

//  방법 1 : Provider 사용
@Service
class MyService {
    private final ObjectProvider<MyLog> myLogProvider;

    public void logic() {
        // DL 은 여러 번 호출 되어도, 같은 생명주기 내에서는 동일한 Bean 보장
        // e.g. Controller, Service 에서 myLogProvider.getObject() 호출 -> 동일한 Bean
        MyLog myLog = myLogProvider.getObject();
        ...
    }
}

@Scope("request")
class MyLog {
    ...
}

방법 2 : 프록시

싱글톤 빈을 띄울 때, 프록시 객체를 주입

실제 동작 => 프록시 메서드 내에서 DL 을 사용하거나 해서 동작할 것으로 예상

proxyMode = ScopedProxyMode.TARGET_CLASS

  • 적용 대상 인터페이스 : INTERFACES 선택
  • 적용 대상 인터페이스 X : TARGET_CLASS 선택
@Service
class MyService {
    private final MyLog myLog;

    public void logic() {
        ...
    }
}

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

}

:star::star: Provider, 프록시 의 핵심 아이디어 : 지연 처리 (꼭 필요한 시점) (다형성 + DI 의 가장 큰 강점)



session 스코프 #

  • HTTP Session 당 유지되는 스코프



application 스코프 #

  • 서블릿컨텍스트(ServletContext) 와 동일한 생명 주기



websocket #

  • 웹 소켓과 동일한 생명 주기




알아둘것 :star::star::star: #

프록시 개념 : 스프링의 핵심 개념

  • 클라이언트(개발자)의 코드 수정 없이 다양한 기능을 부여/조작할 수 있음