1.遇到的问题
@Override
public User findUserById(Integer userId) throws Exception {
Object obj = this.userMapper.selectByPrimaryKey(userId);
System.out.println(obj.getClass().getName());
System.out.println("selectByPrimaryKey.obj.classLoader:"+obj.getClass().getClassLoader());
System.out.println("User.class.classLoader:"+User.class.getClassLoader());
User user = (User) obj;
return null;
}
问题就是当我使用tk.mybatis插件根据userId查询对象时,查询回来后数据不能转换成User。其中user配置与userMapper均为正确
2.分析
那么为什么会出现转换不成功,在这之前我自己也测试通过,但是自添加springboot热部署插件Devtools 就出现这个问题,会不会与Devtools 的添加有关了,然后查阅Devtools 相关信息
这里贴上devtools原理
devtools:是boot的一个热部署工具,当我们修改了classpath下的文件(包括类文件、属性文件、页面等)时,会重新启动应用(由于其采用的双类加载器机制,这个启动会非常快,如果发现这个启动比较慢,可以选择使用jrebel)
- 双类加载器机制:boot使用了两个类加载器来实现重启(restart)机制:base类加载器(简称bc)+restart类加载器(简称rc)。
- bc:用于加载不会改变的jar(eg.第三方依赖的jar)
- rc:用于加载我们正在开发的jar(eg.整个项目里我们自己编写的类)。当应用重启后,原先的rc被丢掉、重新new一个rc来加载这些修改过的东西,而bc却不需要动一下。这就是devtools重启速度快的原因。
双类加载器机制?这不就是用两个classLoader去加载同一个项目的代码吗?这个机制会不会与 双亲委派模型 吗?
然后我打印出各自的classLoader发现确实不一样:
com.sendbp.user.model.User
selectByPrimaryKey.obj.classLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
User.class.classLoader:org.springframework.boot.devtools.restart.classloader.RestartClassLoader@7237dc28
一个是:sun.misc.Launcher$AppClassLoader@18b4aac2
一个是:org.springframework.boot.devtools.restart.classloader.RestartClassLoader@7237dc28
类加载器资料可以参考:
《深入理解Java虚拟机》
https://blog.csdn.net/qq_36642340/article/details/81506435
3.解决方法
在使用 DevTools 时,通用Mapper经常会出现 class x.x.A cannot be cast to x.x.A。
同一个类如果使用了不同的类加载器,就会产生这样的错误,所以解决方案就是让通用Mapper和实体类使用相同的类加载器即可。
DevTools 默认会对 IDE 中引入的所有项目使用 restart 类加载器,对于引入的 jar 包使用 base 类加载器,因此只要保证通用Mapper的jar包使用 restart 类加载器即可。
在 src/main/resources 中创建 META-INF 目录,在此目录下添加 spring-devtools.properties 配置,内容如下:
restart.include.mapper=/mapper-[\\w-\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar
使用这个配置后,就会使用 restart 类加载加载 include 进去的 jar 包。
可以参考:
https://blog.csdn.net/isea533/article/details/70495714
感兴趣的朋友可以关注微信公众号(会定时推送新的知识):