ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Mybatis-plus找不到Mapper接口?排查Spring Boot启动失败的常见原因与解决方案

Mybatis-plus找不到Mapper接口?排查Spring Boot启动失败的常见原因与解决方案 如果你正在经历 Spring Boot 启动失败日志里抛出一大串红色异常提示说找不到 Mapper 接口先别急着砸电脑。这个问题在 Mybatis-plus 项目中出现的频率非常高我几乎每周都能在技术群里看到有人问。而且有意思的是90% 的情况并不是业务代码写错了而是扫描路径、注解配置或模块依赖没配对。这篇文章我把这类问题的常见原因、排查思路、解决方案完整梳理一遍从最简单的情况到多模块工程的复杂场景都覆盖到争取让你看完之后能一次性解决。这个问题有个很迷惑的地方报错信息五花八门有人看到的是 “No qualifying bean of type com.xxx.mapper.UserMapper”有人看到的是 “Invalid bound statement (not found): com.xxx.mapper.UserMapper.selectPage”还有人看到的是 “Field userMapper in com.xxx.service.impl.UserServiceImpl required a bean of type com.xxx.mapper.UserMapper that could not be found”。虽然文本不一样但它们本质上都指向同一件事——Spring 容器里根本没有那个 Mapper 的 Bean 定义或者有 Bean 定义但 Mybatis 没法给它绑定 SQL。所以要解决这个问题最关键的一步是搞清楚 Spring 和 Mybatis-plus 到底是怎么把 Mapper 接口变成可注入的对象的。1. 先搞清楚Mybatis-plus 无法找到 mapper 接口时报错到底长什么样1.1 最常见的两类报错信息区分在开始排查之前我建议你先仔细看一眼控制台里的报错内容因为不同报错对应的排查方向差别很大。第一类报错是 Spring 容器层面的典型文本长这样*************************** APPLICATION FAILED TO START *************************** Description: Field userMapper in com.example.service.impl.UserServiceImpl required a bean of type com.example.mapper.UserMapper that could not be found. The injection point has the following annotations: - Autowired(requiredtrue) Action: Consider defining a bean of type com.example.mapper.UserMapper in your configuration.或者是Parameter 0 of constructor in com.example.service.impl.UserServiceImpl required a bean of type com.example.mapper.UserMapper that could not be found.这类报错的核心信息是 “required a bean of type ... that could not be found”意思是 Spring 在启动时做依赖注入发现UserService里有个UserMapper字段要注入但容器里没有这个类型的 Bean。第二类报错是 Mybatis 运行层面的典型文本是org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.example.mapper.UserMapper.selectPage这类报错的意思是Mapper 接口本身已经被扫描到了Spring 容器里也有代理对象但是 Mybatis 在解析 Mapper 接口时发现接口里声明的方法找不到对应的 SQL 语句。区分这两类报错很重要第一类要找的是“为什么没被扫描到”“为什么没注册成 Bean”第二类要找的是“为什么 XML 没绑定上”或“注解 SQL 没解析出来”。1.2 为什么 Spring 会报“找不到这个 Bean”很多人不理解明明我的 Mapper 接口写了Mapper注解为什么 Spring 还找不到问题出在 Mybatis-plus 对 Mapper 接口的处理方式上。Mapper 接口本身是一个接口Spring 默认不会为接口创建 Bean。Mybatis-plus 通过MapperScan注解或Mapper注解触发扫描把每个 Mapper 接口动态生成一个代理对象MapperProxy然后通过FactoryBean的形式注册到 Spring 容器里。所以“找不到 Bean”这件事本质上是扫描器没有扫描到你的 Mapper 接口或者扫描到了但没有成功注册。只要顺着这条线索往下查问题基本都能定位。2. 最常见的 4 个坑扫描路径、注解、依赖、编译产物2.1 启动类位置和扫描路径不匹配这是最最最常见的坑没有之一。SpringBootApplication注解默认会扫描启动类所在包以及所有子包。这句规则看起来很简单但实际项目中非常容易踩雷。举个例子假设你的项目结构是这样com.example.demo ├── DemoApplication.java // 启动类在 com.example.demo 下 ├── controller ├── service └── mapper如果DemoApplication.java在com.example.demo包下UserMapper在com.example.demo.mapper包下那么默认扫描可以扫到正常情况下不会出问题。但很多实际项目的结构是这样的com.example ├── admin │ ├── AdminApplication.java // 启动类在 com.example.admin 下 └── common └── mapper └── UserMapper.java // Mapper 在 com.example.common.mapper 下这时问题就来了。AdminApplication默认扫描的是com.example.admin包及其子包根本不会去扫描com.example.common.mapper。Spring 容器里自然没有UserMapper这个 Bean启动时就会报 “required a bean of type ... that could not be found”。解决方式有两种第一种在启动类或任意配置类上加上MapperScan明确指定要扫描的包路径SpringBootApplication MapperScan(com.example.common.mapper) public class AdminApplication { public static void main(String[] args) { SpringApplication.run(AdminApplication.class, args); } }第二种把SpringBootApplication的scanBasePackages属性扩展一下SpringBootApplication(scanBasePackages {com.example}) public class AdminApplication { public static void main(String[] args) { SpringApplication.run(AdminApplication.class, args); } }注意这两种方式是有区别的。MapperScan只影响 Mybatis 对 Mapper 接口的扫描scanBasePackages影响的是 Spring 对所有组件的扫描Controller、Service 等。如果你的 Service 也在com.example.common包下光靠MapperScan是不够的还要把scanBasePackages一并配好。2.2 Mapper 和 MapperScan 到底怎么配合很多新手搞不清楚Mapper和MapperScan的关系这里我一句话总结如果加了MapperScan指定包路径下的所有 Mapper 接口都会自动被扫描注册接口上不需要再加Mapper。如果不加MapperScan那每个 Mapper 接口都必须显式加上Mapper注解否则不会被扫描。两个都加也不会冲突MapperScan范围更大是更推荐的做法。还有一个细节MapperScan支持写多个包路径也支持通配符。MapperScan({com.example.mapper, com.example.common.mapper})或者扫描一个父包让所有子包都覆盖到MapperScan(com.example.**.mapper)这里的**是 Mybatis 扫描器支持的 Ant 风格通配符*.mapper只管一层**.mapper可以匹配多层目录。在实际项目中我会明确建议统一走MapperScan不要依赖Mapper。原因很简单在一个有几十个 Mapper 接口的项目里如果每个人都记得在接口上加Mapper那没问题但只要有人忘了加就会出现今天讨论的这个报错。用MapperScan一次性扫一片省心得多。2.3 Mapper 接口所在的包没有被当前模块依赖这个问题在多模块工程里尤其突出。很多公司项目都会拆成好几层大概长这样parent ├── demo-common // 公共模块实体类、Mapper 接口 ├── demo-service // 业务模块Service 实现 └── demo-web // Web 启动模块Controller、启动类常见的错误是UserMapper接口写在demo-common模块里但demo-web模块的pom.xml只依赖了demo-service而demo-service的pom.xml里没有声明依赖demo-common或者声明了但漏了。这样一来demo-web启动时demo-common里的UserMapper.class根本不在 classpath 中Spring 扫描的时候自然找不到。这种场景的排查方法很明显在demo-web模块的pom.xml里加上对demo-common的依赖dependency groupIdcom.example/groupId artifactIddemo-common/artifactId version${project.version}/version /dependency如果你直接依赖了demo-service而demo-service内部依赖了demo-common那最好是确认一下demo-service的 pom 里有没有把demo-common的 scope 设置成optional或者provided。如果设置了依赖就不会传递到更上层的demo-web模块里也会出现类似问题。用一个命令可以快速验证类是否在 classpath 中在项目根目录执行mvn dependency:tree然后看输出里有没有demo-common的依赖记录。如果看不到说明确实没有引进来。2.4 编译产物问题target 目录里的 class 没更新这个坑看起来很低级但发生频率很高尤其是老项目。具体现象是代码明明没问题MapperScan也加了依赖也引了但启动还是报找不到 Mapper。大多数时候是 IDE 的增量编译出了问题target/classes目录下的成旧 class 文件还在新编译的类没有覆盖进去。或者更离谱的是整个模块的 class 文件压根没有生成。遇到这种情况最直接的办法就是先做一次彻底的清理重编译mvn clean mvn compile或者直接mvn clean package -DskipTests在 IDEA 里也可以直接点Build - Rebuild Project或者手动删掉根目录下的target文件夹再重新编译。注意如果项目里用了 Lombok还要留意一下 Lombok 插件和注解处理器是否正常工作。很多奇怪的问题包括编译出来的类不完整、只生成了接口没有生成实现等都和 Lombok 有关。比如热词里提到的“mybatis-plus多模块 lombok插件”说明这类组合是很容易出问题的组合。还有一个小技巧编译完以后去target/classes目录下看一眼找到你的 UserMapper.class 文件。如果这个 class 文件存在说明类本身没问如果不存在那问题就出在编译阶段不用查 Spring 的配置。3. 若依这类多模块框架遇到此问题的特殊排查法3.1 为什么若依框架里特别容易踩这个坑如果你用的是若依RuoYi框架或者基于若依二次开发的项目这个问题几乎是“必踩坑”的。原因很现实若依的模块划分比较细而且启动类所在的包结构和 Mapper 所在的包结构常常不在同一个父包下。典型的若依模块结构大概是这样的ruoyi ├── ruoyi-admin // Web 启动模块 ├── ruoyi-common // 公共模块很多 Mapper 在这里 ├── ruoyi-framework // 框架配置模块 └── ruoyi-system // 系统模块也有大量 Mapper而启动类RuoYiApplication在ruoyi-admin模块的com.ruoyi包下。虽然看起来都在com.ruoyi下但若依的项目结构里很多 Mapper 接口是分散在不同业务 jar 中的比如ruoyi-system模块里的SysUserMapper如果在启动时没有通过MapperScan把ruoyi-system的 mapper 包路径扫进来那就一定报找不到。如果是自己单独建的项目仿照若依结构写的也容易出现同样的情况。因为你把代码搬到了不同的包名下但只复制了注解配置没有去适配新的包路径。3.2 若依框架下的标准配置长什么样以若依框架为准它的启动类通常长这样SpringBootApplication(exclude {DataSourceAutoConfiguration.class}) public class RuoYiApplication { public static void main(String[] args) { SpringApplication.run(RuoYiApplication.class, args); } }然后 Mapper 的扫描是通过在ruoyi-framework模块的某个配置类上加上MapperScan来实现的Configuration MapperScan(com.ruoyi.**.mapper) public class MyBatisConfig { // ... }注意com.ruoyi.**.mapper这种写法它表示扫描com.ruoyi包下所有子包里以.mapper结尾的包路径。在若依这种多模块、多业务包的结构下这么写基本能覆盖所有 Mapper。如果你把业务代码放到了com.company.business之类的包下那com.ruoyi.**.mapper就扫不到了必须加上你自己业务的包路径MapperScan({com.ruoyi.**.mapper, com.company.business.**.mapper})3.3 多模块启动时报错的完整排查顺序在多模块工程里遇到这个报错我建议按以下顺序排查每一步都很快可以快速缩小范围确认 Mapper 接口所在的模块被当前启动模块依赖了。看pom.xml或者执行mvn dependency:tree -Dincludes模块groupId:模块artifactId。确认启动类上的MapperScan如果有包含了 Mapper 所在的包路径。确认 Mapper 接口的编译产物存在去target/classes下找.class文件。在启动类里临时加一个ApplicationRunner或CommandLineRunner打印容器里所有 Mapper 相关的 BeanSpringBootApplication public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context SpringApplication.run(DemoApplication.class, args); String[] beanNames context.getBeanNamesForAnnotation(Mapper.class); System.out.println(Mapper beans: Arrays.toString(beanNames)); } }这个操作能直接告诉你Spring 容器里到底注册了哪些 Mapper。如果这个列表是空的说明扫描器没起作用问题一定出在扫描配置上。如果列表里有 UserMapper那问题就可能出在 XML 绑定上继续看下面一节。4. XML 映射文件无法绑定一个容易被误判的“假兄弟问题”4.1 Invalid bound statement 和找不到 Mapper 的区别前面提到过有一部分人报“找不到 Mapper”实际日志里是Invalid bound statement (not found)。这类问题和“Spring 容器里没有这个 Bean”是两回事但很多人在网上搜索时把这两类混在一起容易被误导。Invalid bound statement的意思是Mybatis 已经成功扫描到 UserMapper 接口也生成了代理对象但当你调用某个方法时Mybatis 找不到这个方法对应的 SQL。常见原因有以下几种Mapper 接口对应的 XML 文件没有被加载到 classpath 中。XML 文件里 namespace 写错了和 Mapper 接口的全限定名不一致。Mapper 接口方法名和 XML 里的 id 对应不上。接口方法是注解 SQL如Select但 XML 和注解同时存在在某些配置下导致冲突。4.2 如何正确配置 mapper-locationsMybatis-plus 中XML 文件默认的加载路径是classpath*:/mapper/**/*.xml。如果你的 XML 文件放在src/main/resources/mapper目录下且文件名例如UserMapper.xml那么一般不需要额外配置。但如果你的 XML 文件放在其他目录或者你的多模块工程里 XML 散落在各个 jar 包中就必须显式配置mybatis-plus: mapper-locations: - classpath*:mapper/**/*.xml - classpath*:/com/example/**/mapper/**/*.xml注意classpath*:和classpath:的区别。classpath:只会从当前 classpath 中找一个匹配的文件classpath*:则会扫描所有 jar 包中的 classpath 路径。多模块工程里Mapper 的 XML 经常在各模块的 jar 包中用classpath*:会更稳妥。还有一个特殊情况Mapper 接口在demo-common模块的com.example.common.mapper包下XML 文件也放到了同一个包目录下——比如src/main/java/com/example/common/mapper/UserMapper.xml。这种放法有一个大坑Maven 默认不会把src/main/java目录下的 XML 文件打进产物中。你必须在pom.xml里增加配置build resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes /resource /resources /build不过说实话我不建议把 XML 放到 java 目录下。统一放到src/main/resources里用mapper/目录管理后续找问题和维护都会轻松很多。4.3 不用 XML纯注解的时候为什么也会报绑定失败有一些场景下你的 Mapper 接口根本没有对应的 XML 文件方法上直接用了Select注解public interface UserMapper extends BaseMapperUser { Select(SELECT * FROM user WHERE id #{id}) User selectByIdCustom(Long id); }这种情况下如果也报Invalid bound statement常见原因有两种。第一种Mybatis-plus在解析 Mapper 时既要在 XML 中找方法对应的 SQL也允许从注解中读取 SQL。如果 XML 里没有这个方法而注解里的 SQL 因为某种原因没被识别比如同时引入了mybatis-plus-boot-starter和原版mybatis-spring-boot-starter导致两个 Mybatis 版本打架就会报找不到绑定。第二种使用了 Lombok 的Builder或Accessors(chain true)导致 Mybatis 反射生成实体的 MetaObject 时某些属性 getter/setter 不存在从而在解析 Mapper 方法时出现异常表象上也会接近绑定失败。所以我才建议当Invalid bound statement出现时第一件事不是马上改 XML 配置而是先确认项目中是不是只引入了一个 Mybatis 依赖。如果同时引入了dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId /dependency那毫无疑问必然出问题。删掉其中一个只保留mybatis-plus-boot-starter问题就会消失。5. 一套可直接照做的排查手册从最快到最全5.1 两分钟快速定位法如果只想快速解决问题我建议你按下面这个清单走每一步确认时间不超过三十秒找到报错里提示的 Mapper 接口比如UserMapper。看你的项目里有没有使用MapperScan。有看它的包路径是否覆盖了 UserMapper 所在的包。没去检查 UserMapper 接口上有没有Mapper注解。检查 UserMapper 所在的模块是否被启动模块依赖。执行mvn clean然后重新启动项目。这一套走下来绝大多数情况都能解决。如果还没解决继续往下走。5.2 用 ApplicationContext 做一次“体检”如果你不想靠猜我教你一个直接看到真相的方法。在启动类里临时加几行代码把 Spring 容器里所有 Mybatis 相关的 Bean 打印出来一眼就能看出有没有扫描到 MapperSpringBootApplication public class DemoApplication implements ApplicationRunner { Autowired private ApplicationContext applicationContext; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } Override public void run(ApplicationArguments args) { MapString, Object mapperBeans applicationContext.getBeansWithAnnotation(Mapper.class); System.out.println(Mapper beans size mapperBeans.size()); mapperBeans.forEach((k, v) - System.out.println( - k - v.getClass().getName())); } }如果你发现mapperBeans.size()是 0说明 Mybatis 完全没扫描到你的 Mapper 接口返回去查MapperScan路径。如果有 UserMapper但启动还是报错那问题转移到了 XML 绑定或依赖注入层面。这时候再把注意力放到mapper-locations配置和Invalid bound statement那类问题上去。5.3 日志级别临时调高看 Mybatis 扫描日志还有一个很实用的方式把logging.level.com.baomidou.mybatisplus和logging.level.org.apache.ibatis调成 DEBUG然后重新启动。Mybatis 扫描器在 DEBUG 模式下会打印它扫描了多少个接口、分别是什么。这样你就能从日志里直观地看到扫描范围是否包含理想的包路径。logging: level: com.baomidou.mybatisplus: debug org.apache.ibatis: debug不过要注意日志量大平时不要开着 DEBUG 跑生产环境只在排查时临时开一下。6. 常见问题速查表按报错场景直接对号入座为了方便查阅我把这类“找不到 mapper 接口”相关的典型场景整理成一个表格你可以按自己的实际情况快速定位。报错现象根因解决方式Field xxxMapper ... required a bean of type ... that could not be foundMapperScan包路径不对或启动类默认扫描不到 Mapper 所在包检查启动类位置和MapperScan路径覆盖到 Mapper 所在包Field xxxMapper ... required a bean of type ... that could not be found多模块下Mapper 所在模块没有被启动模块依赖在启动模块的 pom.xml 中添加对 Mapper 所在模块的依赖Invalid bound statement (not found): com.xxx.mapper.UserMapper.selectPageXML 文件未被加载或 namespace 写错或 mapper-locations 配置不正确检查 XML 位置和 mapper-locations 配置确保 namespace 等于接口全限定名Invalid bound statement (not found): com.xxx.mapper.UserMapper.selectPageMaven 没有把 java 目录下的 XML 文件打入产物在 pom.xml 的 resources 中增加 XML 文件打包配置或把 XML 移到 resources 目录启动时没有报错运行时注入 Mapper 为 null多模块中部分包被 exclude 掉了或存在多个MapperScan相互干扰统一入口只保留一个扫描配置检查 exclude 配置IDEA 中启动报错但命令行 mvn spring-boot:run 正常IDEA 缓存或增量编译问题IDEA 中执行Build - Rebuild Project或删除 target 目录后重启这个表格基本覆盖了我在实际工作中遇到过的全部情况。如果你按表格对号入座后还没解决那大概率是你项目里有某个特殊的自定义配置在捣乱这时候就要去看启动过程的完整日志了。7. 排查这类问题的3点个人心得最后分享几个我自己的习惯虽然不能解决所有问题但能帮你减少踩这类坑的概率。第一我建议所有新项目的启动类都加上MapperScan并且扫描路径写得尽量精确不要为了省事直接写个根包名。根包名确实能覆盖所有 Mapper但会导致启动时多扫描很多无关的包而且如果以后换了包结构排查起来更麻烦。第二Mapper 接口的 XML 文件一定要放到 resources 目录下并且路径结构尽量和接口包名一致。比如UserMapper在com.example.mapper包那么 XML 就放在resources/mapper/UserMapper.xml然后用classpath*:mapper/**/*.xml加载。这是最不容易出错的组合。第三遇到问题别急着百度复制代码。先把MapperScan路径、pom 依赖、编译产物三件事验证一遍比盲目改配置有效得多。我见过太多人往启动类上塞各种 EnableXxx 注解结果问题反而越改越乱。根据我个人的经验绝大多数“Mybatis-plus 无法找到 mapper 接口”的求助帖最后基本上都是扫描路径或模块依赖的问题解决方案就是我上面写的这些。如果照着这文章排查一遍还找不到问题那就把启动日志完整发出来上面每一行红字都有它的含义一条条拆开看一定能定位到根因。
返回列表