启用SpringDocs时OpenTelemetry Spring Boot Starter的ParameterNameDiscoverer Bean冲突问题求助
大家好,我最近在给Spring Boot应用配置通过OTLP导出日志、追踪和指标,用了官方的OpenTelemetry Spring Boot Starter,按照文档要求导入了对应的BOM和依赖,大部分配置看起来都没问题,但启动应用时遇到了Bean冲突的错误,具体情况如下:
启动报错日志
16:44:39.629 [restartedMain] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'otelInstrumentationWithSpanAspect' defined in class path resource [io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/annotations/InstrumentationAnnotationsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'otelInstrumentationWithSpanAspect' parameter 1: No qualifying bean of type 'org.springframework.core.ParameterNameDiscoverer' available: expected single matching bean but found 2: parameterNameDiscoverer,localSpringDocParameterNameDiscoverer 16:44:39.635 [restartedMain] INFO i.o.sdk.logs.SdkLoggerProvider - Calling shutdown() multiple times. 16:44:39.636 [restartedMain] INFO i.o.e.internal.grpc.GrpcExporter - Calling shutdown() multiple times. 16:44:39.636 [restartedMain] INFO i.o.sdk.metrics.SdkMeterProvider - Multiple close calls 16:44:39.636 [restartedMain] INFO i.o.e.internal.grpc.GrpcExporter - Calling shutdown() multiple times. 16:44:39.636 [restartedMain] INFO i.o.sdk.trace.SdkTracerProvider - Calling shutdown() multiple times. 16:44:39.636 [restartedMain] INFO i.o.e.internal.grpc.GrpcExporter - Calling shutdown() multiple times. 16:44:39.637 [restartedMain] INFO o.a.catalina.core.StandardService - Stopping service [Tomcat] 16:44:39.649 [restartedMain] INFO o.s.b.a.l.ConditionEvaluationReportLogger - Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 16:44:39.657 [restartedMain] ERROR o.s.b.d.LoggingFailureAnalysisReporter - *************************** APPLICATION FAILED TO START *************************** Description: Parameter 1 of method otelInstrumentationWithSpanAspect in io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations.InstrumentationAnnotationsAutoConfiguration required a single bean, but 2 were found: - parameterNameDiscoverer: defined by method 'parameterNameDiscoverer' in class path resource [io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/annotations/InstrumentationAnnotationsAutoConfiguration.class] - localSpringDocParameterNameDiscoverer: defined by method 'localSpringDocParameterNameDiscoverer' in class path resource [org/springdoc/core/configuration/SpringDocConfiguration.class] Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
问题分析
从报错信息能明确看出,OpenTelemetry的InstrumentationAnnotationsAutoConfiguration和SpringDoc的SpringDocConfiguration各自定义了一个ParameterNameDiscoverer类型的Bean,而OpenTelemetry的otelInstrumentationWithSpanAspect方法在注入时需要唯一的该类型Bean,因此触发了依赖注入冲突。
可行的解决办法
我整理了几个实用的解决方案,大家可以根据自己的需求选择:
方案一:标记其中一个Bean为@Primary
自定义一个配置类,将其中一个ParameterNameDiscovererBean标记为优先使用的Primary Bean。比如如果想优先使用OpenTelemetry自带的Bean,可以这么写:@Configuration public class PrimaryParameterNameDiscovererConfig { @Primary @Bean public ParameterNameDiscoverer primaryParameterNameDiscoverer( @Qualifier("parameterNameDiscoverer") ParameterNameDiscoverer otelParameterNameDiscoverer) { return otelParameterNameDiscoverer; } }如果想优先使用SpringDoc的Bean,只需要把
@Qualifier的值改成localSpringDocParameterNameDiscoverer即可。方案二:通过@Qualifier指定注入的Bean
我们可以自定义配置类,覆盖OpenTelemetry的otelInstrumentationWithSpanAspectBean的创建逻辑,明确指定要使用的ParameterNameDiscoverer:@Configuration @AutoConfigureBefore(InstrumentationAnnotationsAutoConfiguration.class) public class OtelAspectOverrideConfig { @Bean public OtelInstrumentationWithSpanAspect otelInstrumentationWithSpanAspect( Tracer tracer, @Qualifier("parameterNameDiscoverer") ParameterNameDiscoverer parameterNameDiscoverer, @Value("${otel.instrumentation.spring.annotations.enabled:true}") boolean enabled) { return new OtelInstrumentationWithSpanAspect(tracer, parameterNameDiscoverer, enabled); } }这个方案更明确,直接指定OpenTelemetry使用自己定义的
ParameterNameDiscoverer,避免冲突。方案三:排除冲突的自动配置类(不推荐)
如果确认不需要其中某一方的ParameterNameDiscoverer,可以在@SpringBootApplication注解中排除对应的自动配置类,比如排除SpringDoc的配置:@SpringBootApplication(exclude = SpringDocConfiguration.class)但这个方法风险较高,可能会导致SpringDoc的功能异常,除非你确定不需要SpringDoc的相关功能,否则不建议使用。
总结
优先推荐方案一或方案二,这两个方法都能在不影响原有功能的前提下解决Bean冲突问题,方案一更简洁,方案二更精准。
备注:内容来源于stack exchange,提问作者vkourt

