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

김원종·2023년 7월 12일
0

싱글톤 빈과 프로토타입 빈을 함께 사용할 때, 어떻게 하면 사용할 때 마다 항상 새로운 프로토타입 빈을 생성할 수 있을까?

스프링 컨테이너에 요청

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


  • 실행해보면 ac.getBean()을 통해서 항상 새로운 프로토타입 빈이 생성되는 것을 확인할 수 있다.
  • 의존관계를 외부에서 주입(DI)받는게 아니라 이렇게 직접 필요한 의존관계를 찾는 것을 Dependency Lookup(DL)의존관계 조회(탐색) 이라한다.
  • 그런데 이렇게 스프링의 애플리케이션 컨텍스트 전체를 주입받게 되면, 스프링 컨테이너에 종속적인 코드가 되고, 단위 테스트도 어려워진다.
  • 지금 필요한 기능은 지정한 프로토타입 빈을 컨테이너에서 대신 찾아주는 딱 DL 정도의 기능만 제공하는 무언가가 있으면 된다.

ObjectFactory, ObjectProvider
지정한 빈을 컨테이너에서 대신 찾아주는 DL서비스를 제공하는 것이 바로 ObjectProvider이다. 참고로 과거에는 ObjectFactory가 있었는데 여기에 편의 기능을 추가해서 ObjectProvider가 만들어졌다.

  • 실행해보면 prototypeBeanProvider.getObject()을 통해서 항상 새로운 프로토 타입 빈이 생성되는 것을 확인할 수 있다.
  • ObjectProvidergetObject()를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다. ( DL )
  • 스프링이 제공하는 기능을 사용하지만 , 기능이 단순하므로 단위테스트를 만들거나 mock코드를 만들기는 훨씬 쉬워진다.
  • ObjectProvider는 지금 딱 필요한 DL정도의 기능만 제공한다.

특징

  • ObjectFactory : 기능이 단순 , 별도의 라이브러리 필요 없음, 스프링에 의존
  • ObjectProvider: ObjectFactory 상속 , 옵션 , 스트림 처리등 편의 기능이 많고, 별도의 라이브러리 필요 없음, 스프링에 의존

JSR-330 Provider

마지막 방법은 javax.inject.Provider 라는 JSR-330 자바 표준을 사용하는 방법이다.
이 방법을 사용하려면 javax.inject:javax.inject:1 라이브러리를 gradle에 추가해야한다.

public interface Provider<T>    {/**
 * Provides a fully-constructed and injected instance of {@code T}.
 * @return instance of {@code T}.
 *
 * @throws RuntimeException if the injector encounters an error while
 *  providing an instance. For example, if an injectable member on
 *  {@code T} throws an exception, the injector may wrap the exception
 *  and throw it to the caller of {@code get()}. Callers should not try
 *  to handle such exceptions as the behavior may vary across injector
 *  implementations and even different configurations of the same injector.
 */
T get();        }

  • 실행해보면provider.get()을 통해서 항상 새로운 프로토타입 빈이 생성되는 것을 확인할 수 있다.
  • providerget()을 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.( DL )
  • 자바 표준이고,기능이 단순하므로 단위테스트를 만들거나 mock코드를 만들기는 훨씬 쉬워진다.
  • Provider는 지금 딱 필요한 DL정도의 기능만 제고한다.
  • 장점도 심플하다이고 단점도 심플하다 이다!!

특징

  • get() 메서드 하나로 기능이 매우 단순하다.
  • 별도의 라이브러리가 필요하다
  • 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용할 수 있다.
  • 스프링 부트 3.x부터는
    implementation 'jakarta.inject:jakarta.inject-api:2.0.1'
    이걸 추가해줘야한다.

정리

  • 그러면 프로토타입 빈을 언제 사용할까? 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다. 그런데 실무에서 웹 애플리케이션을 개발해보면, 싱글톤 빈으로 대부분의 문제를 해결할 수 있기 때문에 프로토타입 빈을 직접적으로 사용하는 일은 매우 드물다.
  • ObjectProvider,JSR330 Provider 등은 프로토타입 뿐만 아니라 DL이 필요한 경우는 언제든지 사용할 수 있다.

| 참고 : 스프링이 제공하는 메서드에 @LOOKUP애노테이션을 사용하는 방법도 있지만 , 이전 방법들로 충분하고 , 고려해야할 내용도 많아서 생략!

| 참고 : 실무에서 자바 표준인 JSR-330 Provider를 사용할 것인지, 아니면 스프링이 제공하는 ObjectProvider를 사용할 것인지 고민이 될 것이다. ObjectProvider는 DL을 위한 편의 기능을 많이 제공해주고 스프링 외에 별도의 의존관계 추가가 필요 없기 때문에 편리하다. 만약 ( 정말 그럴일은 거의 없겠지만 )코드를 스프링이 아닌 다른 컨테이너에서도 사용할 수 있어야 한다면 JSR-330 Provider를 사용해야한다.
스프링을 사용하다 보면 이 기능 뿐만 아니라 다른 기능들도 자바 표준과 스프링이 제공하는 기능이 겹칠때가 많이 있다. 대부분 스프링이 더 다양하고 편리한 기능을 제공해주기 때문에 , 특별히 다른 컨테이너를 사용할 일이 없다면, 스프링이 제공하는 기능을 사용하면 된다.

profile
개린이

0개의 댓글

관련 채용 정보