mapper-locations 和 @MapperScan 是 MyBatis/MyBatis-Plus 中分工完全不同 的两个配置:@MapperScan 管「Mapper接口的注册」,mapper-locations 管「XML文件的加载」,二者缺一不可,且无直接关联,以下是详细区别和协同关系:
一、核心区别(总览)
| 维度 | @MapperScan | mapper-locations |
|---|---|---|
| 归属层面 | Spring + MyBatis整合层(mybatis-spring) | MyBatis核心层(mybatis/mybatis-plus) |
| 作用对象 | Java接口(Mapper接口,如MsWorkflowDao) | XML文件(如MsWorkflowDao.xml) |
| 配置形式 | 注解(加在启动类/配置类上) | 配置项(yml/properties中) |
| 核心目标 | 扫描Mapper接口,为其创建代理对象并注册到Spring容器,让@Autowired能注入 |
扫描XML文件,解析其中的SQL语句,绑定到Mapper接口的方法上 |
| 失败后果 | 注入Mapper接口时报NoSuchBeanDefinitionException |
调用Mapper方法时报Invalid bound statement (not found) |
| 多模块关键配置 | 扫描路径覆盖所有子模块的Mapper包(如com.example.**.dao) |
用classpath*:替代classpath:,扫描所有依赖jar中的XML |
二、详细拆解
1. @MapperScan:管「Mapper接口的注册」(Spring层面)
你可以把它理解为:告诉Spring"去哪里找Mapper接口,并为这些接口创建可注入的代理对象"。
-
核心行为:
扫描指定包下的所有Mapper接口(如com.example.**.dao下的MsWorkflowDao),通过MyBatis的MapperFactoryBean为每个接口创建MapperProxy代理对象,并将代理对象注册到Spring容器中。 -
核心价值:
没有它,你在Service中@Autowired MsWorkflowDao时,Spring会找不到这个Bean,直接报错; -
示例:
java// 扫描com.example下所有层级的dao包中的Mapper接口 @MapperScan("com.example.**.dao") @SpringBootApplication public class MsApplication {}
2. mapper-locations:管「XML文件的加载」(MyBatis层面)
你可以把它理解为:告诉MyBatis"去哪里找XML文件,解析里面的SQL并绑定到Mapper接口方法上"。
-
核心行为:
根据配置的路径扫描XML文件,解析XML中的<select>/<insert>等标签,将SQL语句与「namespace(Mapper接口全类名)+ id(接口方法名)」绑定,存入MyBatis的MapperRegistry中。 -
核心价值:
没有它,即使Mapper接口已被Spring注册,调用queryPage方法时,MyBatis也找不到对应的SQL,报「Invalid bound statement」; -
示例:
yamlmybatis-plus: # 多模块下用classpath*:扫描所有依赖jar中的XML mapper-locations: classpath*:com/example/**/dao/*.xml
三、二者的协同关系(缺一不可)
正常调用Mapper方法的流程是:
Service中@Autowired注入Mapper接口 → Spring通过@MapperScan找到接口并返回代理对象 → 调用接口方法 → MyBatis通过mapper-locations加载的XML找到对应SQL → 执行数据库操作
- 只有
@MapperScan,没有mapper-locations:
Mapper接口能正常注入,但调用方法时MyBatis找不到SQL,报Invalid bound statement; - 只有
mapper-locations,没有@MapperScan:
XML被加载,但Mapper接口未被注册到Spring,注入时直接报NoSuchBeanDefinitionException; - 两者都正确配置:
Mapper接口被Spring注册为代理对象,XML中的SQL被MyBatis绑定到接口方法,调用正常。
四、常见误区(重点避坑)
- 误区1 :以为
@MapperScan能扫描XML文件
→ 错!@MapperScan只扫描Java接口,完全不处理XML文件; - 误区2 :以为
mapper-locations能注册Mapper接口
→ 错!mapper-locations只加载XML,不会让Spring注册Mapper接口; - 误区3 :多模块下,
@MapperScan配了com.example.**.dao,但mapper-locations用了classpath:(少了*)
→ 结果:Mapper接口能扫到,但子模块jar中的XML扫不到,仍报Invalid bound statement; - 误区4 :XML文件放在
src/main/java下,只配了mapper-locations却没配置Maven资源扫描
→ 结果:XML未被打包到classpath,mapper-locations再正确也扫不到。
五、总结
| 配置 | 一句话总结 | 核心解决的问题 |
|---|---|---|
| @MapperScan | 让Spring能找到并注入Mapper接口 | 注入Mapper时的NoSuchBeanDefinition |
| mapper-locations | 让MyBatis能找到Mapper接口对应的SQL(XML中) | 调用方法时的Invalid bound statement |
在多模块场景中,需同时保证:
@MapperScan的路径覆盖所有子模块的Mapper接口包;mapper-locations用classpath*:扫描所有子模块jar中的XML文件。