问题
不兼容的bean定义冲突,存在与现注入的bean同名的bean,但不是同类型。
Annotation-specified bean name ‘userDao’ for bean class
[com.fjd.dao.UserDao] conflicts with existing, non-compatible bean
definition of same name and class [com.fjd.dao.impl.UserDaoImpl]
分析
Spring 容器初始化过程中,存在两个同名的 Bean 定义冲突:
com.fjd.dao.UserDao(接口)和 com.fjd.dao.impl.UserDaoImpl(实现类)都被尝试注册为名为 ‘userDao’ 的 Bean。
它们类型不兼容(一个是接口,一个是实现类),导致 Spring 无法处理。
1. 根本原因
- 接口和实现类同名注册:Spring 试图将接口和实现类都注册为同名 Bean(userDao)
- 类型冲突:接口 UserDao 和实现类 UserDaoImpl 都试图使用相同的 Bean 名称
- 扫描范围过宽:@ComponentScan 可能同时扫描了接口和实现类所在的包
2. 常见错误场景
// 错误示例 1:接口被错误注解
@Repository("userDao") // ❌ 错误:接口不应添加组件注解
public interface UserDao {
}
// 错误示例 2:实现类使用相同名称
@Repository("userDao") // ❌ 与接口名称冲突
public class UserDaoImpl implements UserDao {
}
解决方案
方案 1:移除接口上的注解(推荐)
// UserDao.java
// @Repository("userDao") // 移除接口上的注解
public interface UserDao {
// 接口方法
}
// UserDaoImpl.java
@Repository // ✔️ 保留实现类的注解(默认生成名为 userDaoImpl)
public class UserDaoImpl implements UserDao {
// 实现代码
}
方案 2:为接口和实现类指定不同名称
// UserDao.java
@Repository("userDaoInterface") // ✔️ 指定唯一名称
public interface UserDao {
}
// UserDaoImpl.java
@Repository("userDao") // ✔️ 指定不同名称
public class UserDaoImpl implements UserDao {
}
@Component("userService")
//设置bean的作用域
//@Scope("singleton")
public class UserServiceImpl implements UserService{
@Autowired
@Qualifier("userDao")
private UserDao userDao;
@Override
public void save() {
System.out.println("user service running ...");
userDao.save();
}
}
方案 3:限制组件扫描范围
@Configuration
@ComponentScan(
basePackages = "com.fjd",
excludeFilters = @Filter(type = FilterType.REGEX, pattern = "com.fjd.dao.*") // 排除接口包
)
public class AppConfig {
}
注意:MyBatis 用户的特殊处理
如果使用 MyBatis,只需在接口上使用 @Mapper:
// UserDao.java
@Mapper // ✔️ MyBatis 专用注解
public interface UserDao {
@Select("SELECT * FROM users")
List<User> findAll();
}
// 不需要实现类!自动生成代理对象
总结
组件类型 | 正确注解方式 | 错误做法 |
---|---|---|
接口 | 无注解 或 @Mapper(MyBatis) | @Repository/@Component |
实现类 | @Repository/@Service | 与接口同名注册 |
MyBatis Mapper | @Mapper 或 XML 配置 | 添加 Spring 组件注解 |
错误修复步骤
- 检查接口:移除所有接口上的 @Repository、@Component 等注解
- 检查实现类:确保实现类使用默认名称(如 userDaoImpl)或唯一名称,不使用与接口同名的名称
- 验证扫描配置:确保没有扫描接口包
@SpringBootApplication
@ComponentScan(excludeFilters = @Filter(
type = FilterType.ASPECTJ,
pattern = "com.fjd.dao..*" // 排除 dao 包下的接口
))
public class Application { ... }
- MyBatis 用户:替换为 @Mapper 注解
// 在启动类添加
@MapperScan("com.fjd.dao") // 替代 @ComponentScan