3.7 KiB
title, source_type, status, tags, created
| title | source_type | status | tags | created | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Spring @ConditionalOnBean ordering trap — user-defined @Configuration vs autoconfiguration | error-note | raw |
|
2026-06-17 |
Spring @ConditionalOnBean ordering trap
Parent
raw/project-notes/ca-skeleton-operational-contract
현상 (Symptom)
TracingConfig(@Configuration — user-defined)에 @ConditionalOnBean(Tracer.class) + @ConditionalOnMissingBean(SpanErrorRecorder.class) 빈 메서드를 추가했다. Tracer bean은 @AutoConfigureObservability(tracing=true)로 autoconfiguration에서 공급된다.
테스트에서 MicrometerSpanErrorRecorder(SpanErrorRecorder 구현) 빈을 context.getBean(SpanErrorRecorder.class)로 조회하면:
NoSuchBeanDefinitionException: No qualifying bean of type 'dev.caskeleton.shared.tracing.SpanErrorRecorder' available
→ Tracer 빈은 존재(context.getBean(Tracer.class) 성공)하나, @ConditionalOnBean(Tracer.class) 조건은 false로 평가됨.
원인 (Root Cause)
Spring Boot 문서 주의사항: @ConditionalOnBean / @ConditionalOnMissingBean은 bean definition ordering에 민감하다.
- user-defined
@Configuration클래스(예:@Import(TracingConfig.class))는 Spring이 autoconfiguration보다 먼저 처리한다. - 조건 평가 시점(bean definition 등록 단계)에
Tracer는 아직 정의되지 않음 →@ConditionalOnBean(Tracer.class)= false. - 결과적으로
micrometerSpanErrorRecorder빈 메서드 자체가 스킵됨.
Spring 공식 문서 인용:
"When using
@ConditionalOnBeanand@ConditionalOnMissingBeanin component scan configurations, the condition evaluation is not predictable because of the order in which beans are created."
해결 (Fix)
@ConditionalOnBean(Tracer.class) 제거 → ObjectProvider<Tracer> 런타임 조회로 대체:
@Bean
@ConditionalOnMissingBean(SpanErrorRecorder.class)
SpanErrorRecorder micrometerSpanErrorRecorder(ObjectProvider<Tracer> tracerProvider) {
Tracer tracer = tracerProvider.getIfAvailable();
if (tracer == null) {
return SpanErrorRecorder.NOOP;
}
return new MicrometerSpanErrorRecorder(tracer);
}
ObjectProvider.getIfAvailable()은 bean instantiation 시점(모든 bean definition이 등록된 후)에 호출되므로 Tracer autoconfiguration bean을 정확히 조회한다.
정리 (Lessons)
@ConditionalOnBean은@AutoConfiguration에서만 안전하게 autoconfiguration bean을 조건으로 쓸 수 있다.- user-defined
@Configuration+@ConditionalOnBean(autoconfig-provided-bean)= 순서 문제로 항상 false. - 해결 패턴 2가지:
ObjectProvider<T>런타임 lazy resolution (이번 선택).- user-defined config를
@AutoConfiguration으로 전환 +AutoConfiguration.imports등록.
@ConditionalOnMissingBean은 여전히 유효 — 이미 등록된 bean을 체크하는 것이므로 상대적으로 ordering에 덜 민감하다(단, user-defined bean이 autoconfiguration보다 먼저 등록될 것이 보장되어야 함).
재현 환경
- Spring Boot 3.5.x / Micrometer Tracing 1.5.12
TracingConfig(@Configuration,@EnableConfigurationProperties(TracingSettings.class))- Test:
@SpringBootTest+@AutoConfigureObservability(tracing=true)+@EnableAutoConfiguration(exclude=[data-layer]) - TDD red:
TracingActivationContextTest.realSpanErrorRecorderBeanReplacesNoop→NoSuchBeanDefinitionException
Evidence
actually-implemented: ObjectProvider 패턴으로 교체 후TracingActivationContextTestBUILD SUCCESSFUL.locally-verified::app-bootstrap:test전체 green.