스프링 빈을 지연 조회하거나 선택적으로 조회할때 쓰는 유틸리티
<aside> 💡
구분 | ApplicationContext.getBean(...) |
ObjectProvider<T> |
---|---|---|
조회 시점 | 즉시 조회 (바로 Bean 생성/주입됨) | 지연 조회 가능 (필요할 때 가져옴) |
예외 처리 | Bean이 없으면 NoSuchBeanDefinitionException 발생 |
Bean이 없어도 null 또는 Optional처럼 처리 가능 (getIfAvailable() ) |
사용 용도 | 명확히 필요한 Bean을 즉시 가져올 때 | 선택적/지연적으로 Bean을 사용할 때 |
DI 지원 여부 | 일반적으로 직접 주입 잘 안 함 (보통 테스트나 특수 상황) | 주입받아서 필요할 때 .getObject() 로 사용 가능 |
Lazy 기능 | ❌ 없음 | ✅ 있음 (getObject() 호출 시점에 Bean 사용) |
</aside> |
지연 조회
@Autowired
private ObjectProvider<MyService> myServiceProvider;
public void doSomething() {
MyService service = myServiceProvider.getObject(); // 여기서 빈 조회
service.run();
}
빈을 바로 생성하지 않고, getObject()호출 시점에 가져온다.
안전한 조회
MyService service = myServiceProvider.getIfAvailable(() -> new MyService());
빈이 없어도 에러를 내지 않게 기본값을 지정할 수 있다.
스트림 지원
myServiceProvider.stream()
.forEach(service -> service.run());
같은 타입의 여러 빈을 순회하면서 사용할 수 있다.
선택적 의존성 주입
@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);
...
}
}
특정 빈이 있을때만 사용하고 싶은 경우
순환 참조 방지
@Component
public class A {
private final ObjectProvider<B> bProvider;
public A(ObjectProvider<B> bProvider) {
this.bProvider = bProvider;
}
public void call() {
bProvider.getObject().hello();
}
}
바로 빈을 주입하면 순환 참조가 생길 수 있는 경우
지연 로딩
@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();
}
}
무거운 빈을 실제 필요할 때만 로딩하고 싶은 경우
같은 타입의 다수의 빈을 순회할 경우
@Autowired
private ObjectProvider<Handler> handlerProvider;
public void handleAll() {
handlerProvider.stream().forEach(Handler::handle);
}
List<BeanType>
주입과 비슷하지만, stream()
같은 함수형 스타일을 지원AOP 프록시를 반드시 거쳐야 하는 경우
@Transactional
, @Async
같은 AOP 기반 기능은 프록시 객체에서만 동작ObjectProvider
로 실행 시점에 프록시 빈을 가져와 안전하게 사용