Spring注入报错:期望单个匹配Bean却找到2个,原因是什么?
Spring同类型多Bean注入冲突问题
场景复现
我编写了如下Spring配置类,定义了两个类型均为Set<JsonNode>的Bean:
@Configuration public class DataQueue { /** * 存储从PLC读取的{@link SlowVars}的队列 * @return 队列实例 */ @Bean Set<JsonNode> slowVarsQueue() { return new CopyOnWriteArraySet<>(); } /** * 存储{@link JsonNode}事件的队列 * @return 队列实例 */ @Bean Set<JsonNode> eventsQueue() { return new CopyOnWriteArraySet<>(); } }
在需要注入eventsQueue的类中,我使用Lombok的@AllArgsConstructor生成构造函数:
@Log4j2 @RestController @AllArgsConstructor public class EventsApiImpl implements EventsApi { private Set<JsonNode> eventsQueue; private MariaEventService mariaEventService; private EventManipulationService eventManipulationService; private ObjectMapper mapper; // 业务方法省略 }
但Spring启动时抛出如下错误:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'EventsApiImpl': Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'java.util.Set[com.fasterxml.jackson.databind.JsonNode]' available: expected single matching bean but found 2: slowVarsQueue,eventsQueue
明明变量名和目标Bean的名称一致,为什么还是会报错?
原因分析
Spring构造函数注入的默认匹配逻辑是按类型匹配,而非参数名匹配。虽然你将字段名命名为eventsQueue,但Lombok生成的构造函数参数名在默认编译模式下会被丢失(编译后参数名会变成arg0、arg1这类占位符),导致Spring无法通过参数名识别要注入的目标Bean。此时Spring发现存在两个同类型的Set<JsonNode> Bean,无法确定选择哪一个,因此抛出冲突异常。
解决方法
1. 使用@Qualifier指定目标Bean
在字段上添加@Qualifier注解,明确指定要注入的Bean名称:
@Log4j2 @RestController @AllArgsConstructor public class EventsApiImpl implements EventsApi { @Qualifier("eventsQueue") private Set<JsonNode> eventsQueue; private MariaEventService mariaEventService; private EventManipulationService eventManipulationService; private ObjectMapper mapper; // 业务方法省略 }
2. 给目标Bean标记@Primary
如果大部分场景下都需要注入eventsQueue,可以在对应的@Bean方法上添加@Primary注解,让Spring在同类型Bean中优先选择它:
@Configuration public class DataQueue { // 其他Bean定义省略 @Bean @Primary Set<JsonNode> eventsQueue() { return new CopyOnWriteArraySet<>(); } }
3. 开启编译时保留参数名
在构建工具中配置编译选项,保留Java方法的参数名,让Spring能通过构造函数参数名匹配Bean名称:
Maven配置(pom.xml):
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
Gradle配置(build.gradle):
tasks.withType(JavaCompile) { options.compilerArgs << "-parameters" }
配置后,编译生成的class文件会保留构造函数的参数名,Spring就能通过参数名eventsQueue自动匹配对应的Bean。
内容的提问来源于stack exchange,提问作者Stefano Bossi
相关产品推荐
相关产品推荐

