[스프링 핵심 원리 - 기본편] 빈 스코프

지현·2021년 11월 12일
0

스프링

목록 보기
14/32

빈 스코프란?

빈이 존재할 수 있는 범위

  • 싱글톤 : 기본 스코프, 가장 생명주기가 길고 스프링 컨테이너의 생명주기와 같은 주기를 가짐
  • 프로토타입 : 스프링 컨테이너가 요청받고 빈을 만들어서 제공, 의존관계 주입하고 초기화 메서드까지 불러줌 > 클라이언트에게 반환하고 더이상 스프링 컨테이너는 관여하지 않음, 짧은범위의 스코프
  • 웹 관련 스코프 : 웹과 관련된 기능이 들어가야 사용 할 수 있는 스코프
    • request : 고객 요청이 들어올때 생성, 나갈때 까지 유지되는 스코프
    • session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
    • application : 웹의 서블릿 컨텍스트와 같은 범위(긴 범위)로 유지되는 스코프

프로토타입 스코프

싱글톤 빈 요청

  1. 싱글톤 스코프의 빈을 스프링 컨테이너에 요청
  2. 스프링 컨테이너는 본인이 관리하는 스프링 빈을 반환
  3. 이후에 스프링 컨테이너에 같은 요청이 와도 같은 객체 인스턴스의 스프링 빈을 반환

프로토타입 빈 요청

  1. 프로토타입 스코프의 빈을 스프링 컨테이너에 요청
  2. 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 필요한 의존관계를 주입, 초기화 작업
  3. 스프링 컨테이너는 생성한 프로토타입 빈을 클라이언트에 반환, 더이상 관리 X
  4. 이후에 스프링 컨테이너에 같은 요청이 오면 항상 새로운 프로토타입 빈을 생성해서 반환

정리

  • 싱글톤 빈은 스프링 컨테이너 생성 시점에 초기화 메서드가 실행 되지만, 프로토타입 스코프의 빈은 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드도 실행
  • 프로토타입 빈은 스프링 컨테이너에 요청할 때 마다 새로 생성
  • 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리, 관리는 하지 X > @PreDestroy 같은 종료 메서드가 호출되지 않음
  • 생성된 프로토타입 빈은 클라이언트가 관리, 종료 메서드에 대한 호출도
    클라이언트가 직접 해야함

프로토타입 스코프 - 싱글톤 빈과 함께 사용시 문제점

싱글톤 빈에서 프로토타입 빈 사용

  • 싱글톤 빈과 함께 사용할 때는 의도한 대로 잘 동작하지 않음
  • 싱글톤 빈은 생성 시점에만 의존관계 주입을 받기 때문에 프로토타입 빈이 새로 생성되기는 하지만, 싱글톤 빈과 함께 계속 유지됨

1. 싱글톤 clientBean 은 의존관계 자동 주입을 사용, 주입 시점에 스프링 컨테이너에 프로토타입 빈을 요청
2. 스프링 컨테이너는 프로토타입 빈을 생성해서 clientBean에 반환
3. clientBean은 프로토타입 빈을 내부 필드에 보관
3. 클라이언트 A는 clientBean을 스프링 컨테이너에 요청해서 받고, 싱글톤이므로 항상 같은 clientBean이 반환됨
4. 클라이언트 A는 clientBean.logic()을 호출
5. clientBeanprototypeBeanaddCount()를 호출해서 프로토타입 빈의 count를 증가 > count == 1
6. 클라이언트 B는 clientBean을 스프링 컨테이너에 요청해서 받고, 싱글톤이므로 항상 같은 clientBean이 반환
7. 클라이언트 B는 clientBean.logic() 을 호출
8. clientBeanprototypeBeanaddCount()를 호출해서 프로토타입 빈의 count를 증가 > count == 2

clientBean이 내부에 가지고 있는 프로토타입 빈은 이미 과거에 주입이 끝난 빈 > 싱글톤 빈과 함께 계속 유지됨
주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성이 된 것이지, 클라이언트가 사용 할 때마다 새로 생성되는 것이 아님


프로토타입 스코프 - 싱글톤 빈과 함께 사용시 문제 해결 방법

스프링 컨테이너에 요청

가장 간단한 방법은 싱글톤 빈이 프로토타입을 사용할 때 마다 스프링 컨테이너에 새로 요청

  • 의존관계를 외부에서 주입(DI) 받는게 아니라 이렇게 직접 필요한 의존관계를 찾는 것을 Dependency Lookup (DL) 의존관계 조회
  • 스프링의 애플리케이션 컨텍스트 전체를 주입받게 되면 스프링 컨테이너에 종속적인 코드가 되고, 단위 테스트도 어려워짐

ObjectFactory, ObjectProvide

    @Scope("singleton")
    static class ClientBean{

        @Autowired
        private ObjectProvider<PrototypeBean> prototypeBeanProvider;

        public int logic(){
            PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
            prototypeBean.addCount();
            int count=prototypeBean.getCount();
            return count;
        }
    }
  • getObject() 호출하면 그때서야 스프링 컨테이너에서 프로토타입 빈을 찾아서 반환(DL) > 항상 새로운 프로토타입 빈이 생성
    • 직접 찾는것이 아닌 찾아주는 기능만 제공
  • ObjectFactory는 getObject()만 제공, ObjectProvide는 추가 기능도 제공, 둘다 스프링에 의존

JSR-330 Provider

@Autowired
private Provider<PrototypeBean> provider;

public int logic() {
 PrototypeBean prototypeBean = provider.get();
 prototypeBean.addCount();
 int count = prototypeBean.getCount();
 return count;
}
  • provider.get() 을 통해서 항상 새로운 프로토타입 빈이 생성
  • provider의 get() 을 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환(DL)
  • 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용 가능 > 스프링에 의존적이지 않음
  • Provider 는 지금 딱 필요한 DL 정도의 기능만 제공 > 장점도 심플, 단점도 심플

정리

  • 프로토타입 빈은 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용 > 하지만 싱글톤 빈으로 대부분의 문제를 해결할 수 있기 때문에 프로토타입 빈을 직접적으로 사용하는 일은 매우 드묾
  • ObjectProvider , JSR330 Provider 등은 프로토타입 뿐만 아니라 DL이 필요한 경우는 언제든지 사용 가능

웹 스코프

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

종류

  • request : HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리
    • 클라이언트 A의 요청과 클라이언트 B의 http request 요청에 맞춰서 각각 다른 스프링빈이 생성되서 사용됨
    • http request 요청이 들어오고 나갈 때 까지의 라이프 사이클동안은 같은 애가 관리됨
    • 클라이언트 A가 요청을 하면 A 전용 스프링빈이 생성이 되어서 운영되다가 A의 응답이 나가면 소멸
  • session : HTTP Session과 동일한 생명주기를 가지는 스코프
  • application : 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프
  • websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프

request 스코프 예제 만들기

HTTP 요청이 오면 정확히 어떤 요청이 남긴 로그인지 구분하기 어렵기 때문에 request 스코프를 사용하면 좋음


스코프와 프록시

가짜를 우선 집어넣어놓고 실제 기능을 호출하는 시점에서 진짜를 찾아서 동작
CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입



출처
스프링 핵심 원리 - 기본편

0개의 댓글