Spring注入bean发现多个候选bean错误

1.问题描述

image-20240721175229698

报错信息

1
2
3
4
5
6
7
8
9
***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 1 of constructor in com.hmall.trade.service.impl.OrderServiceImpl required a single bean, but 2 were found:
- orderDetailServiceImpl: defined in file [/Users/tom/Desktop/SpringCloud微服务—资料/day03-微服务01/资料/hmall/trade-service/target/classes/com/hmall/trade/service/impl/OrderDetailServiceImpl.class]
- IOrderDetailService: defined in file [/Users/tom/Desktop/SpringCloud微服务—资料/day03-微服务01/资料/hmall/trade-service/target/classes/com/hmall/trade/service/IOrderDetailService.class]

这个错误信息表明在 Spring 容器中有两个候选的 Bean 满足 OrderController 的构造函数参数要求,而 Spring 无法确定应该注入哪一个。这两个候选的 Bean 是 orderServiceImpl 和 IOrderService。

2.解决方法

在 Spring 依赖注入过程中,OrderDetailServiceImpl 类是 IOrderDetailService 接口的一个实现类,当使用接口类型(IOrderDetailService)进行注入时,Spring 容器中可能有多个该接口的实现类,而 Spring 无法自动决定使用哪一个实现类,所以会报错

解决方法有三种,推荐前两种

方法一:使用 @Qualifier 注解

在 OrderController 中使用 @Qualifier 注解明确指定要注入的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

private final ItemClient itemClient;
// private final OrderDetailServiceImpl detailService;
private final IOrderDetailService detailService;
private final CartClient cartClient;

public OrderServiceImpl(ItemClient itemClient, @Qualifier("orderDetailServiceImpl") IOrderDetailService orderDetailService, CartClient cartClient) {
this.itemClient = itemClient;
this.detailService = orderDetailService;
this.cartClient = cartClient;
}
//...
}

方法二:使用 @Primary 注解

在 OrderServiceImpl 类上添加 @Primary 注解,指定它为默认实现。

1
2
3
4
5
@Primary
@Service
public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements IOrderDetailService {

}

方法三:直接使用实现类创建(不推荐)

1
2
3
4
5
6
7
8
9
10
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

private final ItemClient itemClient;
private final OrderDetailServiceImpl detailService;
// private final IOrderDetailService detailService;
private final CartClient cartClient;
//...
}

直接找到该具体类型的 Bean 并注入,不会有歧义,所以不会报错.

虽然直接注入具体实现类可以解决问题,但从设计上考虑,通常推荐依赖接口而不是具体实现类。

3.疑问

为何一个接口一个实现类也会报错,黑马视频里就没有报错???