前言
昨天在项目中使用代码生成器生成了各层面的代码,但是由于未知的原因一直无法调用。经过多方查找后才发现是@MapperScan注解的问题,由于这个藏得比较隐蔽,所以在此记录一下。
问题描述
在接口完成后调用接口,发现无法调用接口,显示错误是
ERROR 1552 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.example.mydemo.service.xxxxxx] with root cause
大致的意思是绑定错误,无法达到映射的意思。无法找到Service包下面的xxxxService实现类。
排查过程
从百度上看,一般可能是mapper绑定失败所致,所以先从排查mapper有关的类和接口甚至文件开始。
- 检查xml文件所在package名称是否和Mapper interface所在的包名一一对应;
- 检查xml的namespace是否和xml文件的package名称一一对应;
- 检查方法名称是否对应;
- 检查配置文件里的mapper路径是否正确
但是这里排查了很多遍,都没有发现相关的问题。同事给了我一个方法,测试接口是否确实被注入了。
@Autowired
private ApplicationContext applicationContext;
@GetMapping("test")
public void test(){
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanNames) {
Object bean = applicationContext.getBean(beanName);
if (bean instanceof Mapper) {
// 处理Mapper的Bean实例
System.out.println(beanName + " is a Mapper bean.");
}
}
}
运行后发现,mapper确实注入了。
所以有可能是Service接口和实现类的问题,按照这个思路进行排查。
然后排查到启动类上面,当时的启动类是这么写的
@MapperScan(value = {
"com.jsb.iot.common",
"com.jsb.iot.stopcar.parking_lot",
"com.jsb.iot.stopcar.order"
})
@SpringBootApplication
public class StopCarApplication {
public static void main(String[] args) {
SpringApplication.run(StopCarApplication.class, args);
}
}
试着给类名后面的包名加上dao以缩小扫描范围。
@MapperScan(value = {
"com.jsb.iot.common",
"com.jsb.iot.stopcar.parking_lot.dao",
"com.jsb.iot.stopcar.order.dao"
})
@SpringBootApplication
public class StopCarApplication {
public static void main(String[] args) {
SpringApplication.run(StopCarApplication.class, args);
}
}
发送请求测试,结果测试成功。搜查后发现原因
原因
@Mapper
org.apache.ibatis.annotations.Mapper;
作用:给该注解下面的接口在编译时生成对应的动态代理类并且注入到Spring容器中。
@MapperScan
org.mybatis.spring.annotation.MapperScan;
作用:在启动类上配置,配置的是持久层接口的包的路径。编译后会把路径下所有的接口都生成动态代理类
包下面的所有接口都会实现代理类,这就意味着在之前的写法中,除了impl类被注入了Spring容器中之外,还注入了一个Service的实现类,于是乎在Controller层调用Service时,并没有真正获取被注入的impl类,所以无法调用mapper接口。这种情况只有运行时才会报错。
总结
@MapperScan指定范围下的所有接口,是所有接口,不论是Mapper接口、还是Service接口、或者是其它什么接口,只要接口是在@MapperScan指定的范围内,Mybatis都会对该接口进行对应的代理实现(并将代理实现类注册进容器中)。所以在使用@MapperScan时,一定要注意指定的范围不能过大。
@Mapper和@MapperScan注解以及共存
在排查问题时,偶然遇到@Mapper和@MapperScan注解的共存情况问题,现在把结果附加上去。
- 只使用@Mapper注解,不使用@MapperScan注解。会扫描@Mapper注解所在接口,生成动态代理类,注入到Spring容器中。
- 只使用@MapperScan注解,不使用@Mapper注解。会扫描@MapperScan注解配置的包下面的接口生成动态代理类,注入到Spring容器中。
- @Mapper、@MapperScan注解都使用,使用@Mapper的接口,如果在@MapperScan注解中有配置包路径,那么可以正常使用。
- @Mapper、@MapperScan注解都使用,使用@Mapper的接口,如果在@MapperScan注解中没有配置包路径,那么会报错,解决办法,就是在@MapperScan注解中配置正确路径下的包即可。
参考
2021120101_@Mapper和@MapperScan注解以及共存_mapperscan和mapper注解-CSDN博客目录1、@Mapper、@MapperScan注解2、报错使用场景1、@Mapper、@MapperScan注解@Mapper注解:使用:直接在接口类上使用,包是:org.apache.ibatis.annotations.Mapper作用:为有此注解的接口生成动态代理类,并且注入到spring容器中。@MapperScan注解:使用:在启动类上配置,配置的是持久层接口的包路径,标注批量生成此包下的接口的动态代理类,并且注入到容器中。2、报错使用场景问题描述:使用_mapperscan和mapper注解https://blog.csdn.net/m0_48983233/article/details/121648122
@MapperScan与@Mapper_@mapperscan和@mapper-CSDN博客@MapperScan与@Mapper@MapperScan与@Mapper背景说明@MapperScan与@Mapper的作用通过@Mapper让Mybatis对接口提供代理实现通过@MapperScan让Mybatis对接口提供代理实现背景说明我们在编写mapper时,只需要编写接口而不需要对其实现,由Mybatis框架对接口提供对应的代理实现类(,并将代理实现类注册进容器中)。但是Mybatis是怎么知道需要对哪些接口进行代理实现呢,就是通过@MapperScan与@Mappe_@mapperscan和@mapperhttps://blog.csdn.net/justry_deng/article/details/124227444文章来源:https://www.toymoban.com/news/detail-786759.html
SpringBoot中Service实现类添加@Service却任然无法注入的问题 - 简书最近一直在研究Spring Boot。从GitHub上下载了一个my-Blog源码,一边看,一边自己尝试去实现,结果掉在坑了,研究了近一周才爬出来,特地来这博客园记录下来,一...https://www.jianshu.com/p/b72a1ffb3672文章来源地址https://www.toymoban.com/news/detail-786759.html
到了这里,关于SpringBoot项目中添加了@Service然而无法注入Service接口的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!