ObjectProvider<T>

스프링 빈을 지연 조회하거나 선택적으로 조회할때 쓰는 유틸리티

<aside> 💡

ApplicationContext.getBean() VS ObjectProvider<T>

구분 ApplicationContext.getBean(...) ObjectProvider<T>
조회 시점 즉시 조회 (바로 Bean 생성/주입됨) 지연 조회 가능 (필요할 때 가져옴)
예외 처리 Bean이 없으면 NoSuchBeanDefinitionException 발생 Bean이 없어도 null 또는 Optional처럼 처리 가능 (getIfAvailable())
사용 용도 명확히 필요한 Bean을 즉시 가져올 때 선택적/지연적으로 Bean을 사용할 때
DI 지원 여부 일반적으로 직접 주입 잘 안 함 (보통 테스트나 특수 상황) 주입받아서 필요할 때 .getObject()로 사용 가능
Lazy 기능 ❌ 없음 ✅ 있음 (getObject() 호출 시점에 Bean 사용)
</aside>

특징

  1. 지연 조회

    @Autowired
    private ObjectProvider<MyService> myServiceProvider;
    
    public void doSomething() {
        MyService service = myServiceProvider.getObject(); // 여기서 빈 조회
        service.run();
    }
    

    빈을 바로 생성하지 않고, getObject()호출 시점에 가져온다.

  2. 안전한 조회

    MyService service = myServiceProvider.getIfAvailable(() -> new MyService());
    

    빈이 없어도 에러를 내지 않게 기본값을 지정할 수 있다.

  3. 스트림 지원

    myServiceProvider.stream()
        .forEach(service -> service.run());
    

    같은 타입의 여러 빈을 순회하면서 사용할 수 있다.


실무에서 사용하는 경우

  1. 선택적 의존성 주입

    @Component
    public class PaymentProcessor {
        private final ObjectProvider<DiscountPolicy> discountPolicyProvider;
    
        public PaymentProcessor(ObjectProvider<DiscountPolicy> discountPolicyProvider) {
            this.discountPolicyProvider = discountPolicyProvider;
        }
    
        public void process(Order order) {
            DiscountPolicy policy = discountPolicyProvider.getIfAvailable(() -> o -> 0);
            int discount = policy.discount(order);
            ...
        }
    }
    

    특정 빈이 있을때만 사용하고 싶은 경우

  2. 순환 참조 방지

    @Component
    public class A {
        private final ObjectProvider<B> bProvider;
    
        public A(ObjectProvider<B> bProvider) {
            this.bProvider = bProvider;
        }
    
        public void call() {
            bProvider.getObject().hello();
        }
    }
    

    바로 빈을 주입하면 순환 참조가 생길 수 있는 경우

  3. 지연 로딩

    @Component
    public class ReportRunner {
        private final ObjectProvider<HeavyReportService> reportServiceProvider;
    
        public ReportRunner(ObjectProvider<HeavyReportService> reportServiceProvider) {
            this.reportServiceProvider = reportServiceProvider;
        }
    
        public void run() {
            HeavyReportService service = reportServiceProvider.getObject(); // 필요할 때만 초기화
            service.generate();
        }
    }
    

    무거운 빈을 실제 필요할 때만 로딩하고 싶은 경우

  4. 같은 타입의 다수의 빈을 순회할 경우

    @Autowired
    private ObjectProvider<Handler> handlerProvider;
    
    public void handleAll() {
        handlerProvider.stream().forEach(Handler::handle);
    }
    
  5. AOP 프록시를 반드시 거쳐야 하는 경우