본문 바로가기
Back-End/Spring

[Spring] 빈 스코프 - 프로토타입 스코프, 싱글톤 빈과 함께 사용 시 Provider 이용하기

by 달의 조각 2022. 3. 26.
이 글은 김영한 님의 스프링 핵심 원리 - 기본편을 수강하며 정리한 글입니다.

 

 

스프링이 제공하는 다양한 스코프

 

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

 


 

빈 스코프 지정하기

 

🌳 컴포넌트 스캔 자동 등록

@Scope("prototype")
@Component
public class HelloBean {

}

 

🌳 수동 등록

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

 


 

프로토타입 스코프

 

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

  • 싱글톤 빈
    • 스프링 컨테이너 생성 시점에 초기화 메서드가 실행
    • 스프링 컨테이너가 관리하기 때문에 스프링 컨테이너가 종료될 때 빈의 종료 메서드가 실행
  • 프로토타입 빈
    • 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드도 실행된다.
    • 스프링 컨테이너가 생성과 의존관계 주입 그리고 초기화까지만 관여하고, 더는 관리하지 않는다.
    • 스프링 컨테이너가 종료될 때 @PreDestroy 같은 종료 메서드가 전혀 실행되지 않는다.
// destroy()가 필요할 경우 클라이언트가 수동으로 직접 호출
prototypeBean1.destroy();
prototypeBean2.destroy();

 


 

프로토타입 빈과 싱글톤 빈 함께 사용하기

 

  clientBean은 싱글톤이므로, 보통 스프링 컨테이너 생성 시점에 함께 생성되고, 의존관계 주입도 발생한다. 주입 시점에 스프링 컨테이너에 프로토타입 빈을 요청하여 내부 필드에 보관한다. (정확히는 참조값을 보관한다.)

여기서 중요한 점이 있는데, clientBean이 내부에 가지고 있는 프로토타입 빈은 이미 과거에 주입이 끝난 빈이다. 주입 시점에 스프링 컨테이너에 요청해서 프로토타입 빈이 새로 생성이 된 것이지, 사용할 때마다 새로 생성되는 것이 아니다.

 

🔮 싱글톤 빈이 프로토타입을 사용할 때마다 스프링 컨테이너에 새로 요청하기

@Autowired //테스트이므로 간단히 필드 주입을 사용했다
private ApplicationContext ac;

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

 

🐬 ObjectProvider

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

@Scope("singleton")
static class ClientBean {

    @Autowired
    private ObjectProvider<PrototypeBean> prototypeBeanProvider;

    public int logic() {
        //getObject() 호출 시 스프링 컨테이너에서 prototypeBean을 찾아서 반환해 준다
        PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        prototypeBean.addCount();
        int count = prototypeBean.getCount();
        return count;
    }
}
  1. getObject()을 통해서 항상 새로운 프로토타입 빈이 생성된다.
  2. 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다. (프로토타입 빈이므로 이때 생성해서 반환한다.) → DL

 

🐬 JSR-330 Provider

자바 표준으로, 스프링이 아닌 다른 컨테이너에서도 사용할 수 있다.

implementation 'javax.inject:javax.inject:1'
@Scope("singleton")
static class ClientBean {

    @Autowired
    private Provider<PrototypeBean> prototypeBeanProvider;

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

댓글