问题
在spring整合mybatis控制创建连接对象和加载mybatis映射配置扫描,将其作为spring的bean进行管理的过程中,无法读取到自动装配的bean。
原因分析
1.查看调用实体类中是否有Dao的自动注入
2.查看MyBatisConfig配置
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.fjd.domain");
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
public MapperScannerConfigurer getSqlSessionFactoryBean(){
MapperScannerConfigurer mscf = new MapperScannerConfigurer();
mscf.setBasePackage("com.fjd.dao");
//mscf.setSqlSessionFactoryBeanName("sqlSessionFactory");
return mscf;
}
}
通过观察发现,AccountDao通过Autowired自动注入了,但是在MyBatis配置的过程中getSqlSessionFactoryBean()方法命名有误,应为sqlSessionFactory
问题核心:Spring 的 Bean 命名机制
1. @Bean 方法名的双重作用
- 创建 Bean:@Bean 方法用于创建和返回 Bean
- 定义 Bean 名称:默认情况下,方法名就是 Bean 的名称
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) {
// 这个方法会创建一个名为 "getSqlSessionFactoryBean" 的 Bean
}
2. MapperScannerConfigurer 的依赖要求
@Bean
public MapperScannerConfigurer getMapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); // 明确指定需要名为 "sqlSessionFactory" 的 Bean
return configurer;
}
3.命名不匹配导致的问题
- 原始状态(失败)
组件 | 名称 | 问题 |
---|---|---|
SqlSessionFactoryBean | getSqlSessionFactoryBean | 实际存在的 Bean 名称 |
MapperScannerConfigurer 要求 | sqlSessionFactory | 期望的 Bean 名称 |
结果 | ❌ 不匹配 | MyBatis 无法找到所需工厂 |
- 修改后(成功)
组件 | 名称 | 结果 |
---|---|---|
SqlSessionFactoryBean | sqlSessionFactory | 实际存在的 Bean 名称 |
MapperScannerConfigurer 要求 | sqlSessionFactory | 期望的 Bean 名称 |
结果 | ✅ 匹配 | MyBatis 可以找到所需工厂 |
4.为什么名称如此重要?
- MapperScannerConfigurer 的工作原理
当配置 MapperScannerConfigurer 时:
configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
它会在 Spring 容器中查找名为 sqlSessionFactory 的 Bean,而不是通过类型查找。
- Spring 容器中的 Bean 查找机制
按名称查找优先于按类型查找
当明确指定名称时,Spring 只按名称查找
如果名称不匹配,即使类型正确也会失败
解决方案
方案 1:修改方法名
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
// 方法名与所需名称一致
}
方案 2:显式指定 Bean 名称
@Bean("sqlSessionFactory") // 显式指定名称
public SqlSessionFactoryBean anyName(DataSource dataSource) {
// 无论方法名是什么,Bean 名称都是 "sqlSessionFactory"
}
方案 3:修改 MapperScannerConfigurer 的配置(不推荐)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.fjd.dao");
configurer.setSqlSessionFactoryBeanName("getSqlSessionFactoryBean"); // 匹配原始名称
return configurer;
}
总结
- 默认命名规则
方法名模式 | 生成的 Bean 名称 | 示例 |
---|---|---|
getXxx() | xxx | getDataSource() → dataSource |
createX() | createX | createSession() → createSession |
其他 | 保持原样 | sqlSessionFactory() → sqlSessionFactory |
- @Bean 方法名决定了默认 Bean 名称
- MapperScannerConfigurer 需要特定名称的 Bean
- 两者不匹配导致 MyBatis 无法找到所需工厂
- 修改方法名使名称一致解决了问题
- 在 Spring 和 MyBatis 整合的项目中,虽然 DAO 接口(如 AccountDao)没有实现类,但通过 MyBatis 的动态代理机制,它仍然可以被自动装配。这是 Spring + MyBatis 整合的核心特性之一。