解决SpringBoot使⽤devtools导致的类型转换异常问题问题:
最近在使⽤新框架SpringBoot + shiro + spring-data-jpa时,为了体验下spring⾃带的热部署⼯具的便捷,于是引⼊了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- optional=true,依赖不会传递,该项⽬依赖devtools;之后依赖myboot项⽬的项⽬如果想要使⽤devtools,需要重新引⼊ -->
<optional>true</optional>
</dependency>
在起初并没遇到什么问题,当使⽤shiro的session管理,⽽且⽤的sessionDao是redis实现的,然后再使⽤Session存取属性时,发现存进去的属性,再取出来后,就会出现类型转换异常ClassCastException
分析:
然后⾃⼰写了⼀⼤推单元测试模拟就是没问题,后来突然意识到会不会是因为ClassLoader不同导致的类型转换异常呢,然后注意了下项⽬启动时加载项⽬中的类使⽤的加载器都是
org.springframework.start.classloader.RestartClassLoader
⽽从shiro session 取出来的对象(从redis中取出经过反序列化)的类加载器都是
sun.misc.Launcher.AppClassLoader
很明显会导致类型转换异常,原来Spring的dev-tools为了实现重新装载class⾃⼰实现了⼀个类加载器,来加载项⽬中会改变的类,⽅便重启时将新改动的内容更新进来,其实其中官⽅⽂档中是有做说明的:
By default, any open project in your IDE will be loaded using the “restart” classloader, and any regular .jar file will be loaded using the “base” classloader. If you work on a multi-module project, and not each module is imported
into your IDE, you may need to customize things. To do this you can create a META-INF/spring-
devtools.properties file. The spring-devtools.properties file can lude. and restart.include.
prefixed properties. The include elements are items that should be pulled up into the “restart” classloader, and the exclude elements are items that should be pushed down into the “base” classloader. The value of the property is
a regex pattern that will be applied to the classpath.
解决:
⽅案⼀、解决⽅案就是在resources⽬录下⾯创建META-INF⽂件夹,然后创建spring-devtools.properties⽂件,⽂件加上类似下⾯的配置:
All property keys must be unique. As long as a property starts with restart.include. lude. it will be
considered. All META-INF/spring-devtools.properties from the classpath will be loaded. You can package files
inside your project, or in the libraries that the project consumes.
⽅案⼆、不使⽤spring-boot-devtools
针对⽅案⼀作⼀个详细的案例进⾏分析说明,以及解决问题
⾸先准备⼀个jar包,⾥⾯包含序列化以及反序列化的功能。
并打包,在springboot项⽬中引⼊
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- 这个包是我⾃⼰创建的序列化以及反序列化⼯具包 -->
<dependency>
<groupId&le</groupId>
<artifactId>devtools-serialization</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
简单的配置下springboot项⽬,并模拟使⽤jar中的序列化⼯具类进⾏处理对象如下
@SpringBootApplication
public class PortalApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(PortalApplication.class, args);
DemoBean demoBean = new DemoBean();
SerializationUtils.serialize(demoBean);
Object deserialize = SerializationUtils.deserialize();
System.out.println(ClassLoader());
//这⾥对象引⽤是Object类型
System.out.println(deserialize);
System.out.Class().getClassLoader());
}
}
如上,是不会报错的,因为Object是bootstrap引导类加载器加载的,因此不会产⽣任何问题,
但是如果改成下⾯这样
/
/...
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(PortalApplication.class, args);
DemoBean demoBean = new DemoBean();
SerializationUtils.serialize(demoBean);
Object deserialize = SerializationUtils.deserialize();
System.out.println(ClassLoader());
//注意这⾥进⾏了⼀次类型强转
System.out.println((DemoBean)deserialize);
System.out.Class().getClassLoader());
}
//...
结果是会抛出:
Exception in thread "restartedMain" flect.InvocationTargetException at
flect.Method.invoke(Method.java:498) at
org.springframework.start.RestartLauncher.run(RestartLauncher.java:49) Caused by:
java.lang.ClassCastException: com.sample.serial.DemoBean cannot be cast to com.sample.serial.DemoBean at com.sample.PortalApplication.main(PortalApplication.java:27) ... 5 more
⽽观察上⾯输出的ClassLoader信息会发现分别为
org.springframework.start.classloader.RestartClassLoader@63059d5a
sun.misc.Launcher$AppClassLoader@18b4aac2
这就是为什么会明明没问题,却仍然抛了个ClassCastException的根源所在。
那么如何解决这个问题呢?
将输出的ClassLoader信息保持⼀致即可,要么都是RestartClassLoader要么都是
AppClassLoader
这⾥参考spring官⽅⽂档给出的配置⽅法进⾏处理。
在resources下创建META-INF/spring-devtools.properties
如图:
下⼀步在spring-devtools.properties添加配置
restart.include.projectcommon=/devtools-serialization-[\\w.-]+.jar
注意这⾥我需要包含的jar包名称为devtools-serialization-1.0-SNAPSHOT.jar
配置的key以restart.include.开头即可
restart.include.*
value 为⼀个正则表达式
下⾯再次运⾏程序查看效果:
没有异常产⽣
控制台输出classLoader信息为
org.springframework.start.classloader.RestartClassLoader@1d9fbdd4 DemoBean{age=null,
name='null'} org.springframework.start.classloader.RestartClassLoader@1d9fbdd4
问题完美解决。
补充知识:Springboot+devtools配置热部署
Spring Boot提供了spring-boot-devtools这个模块来使应⽤⽀持热部署,可以提⾼开发者的开发效率,⽆需⼿动重启Spring Boot应⽤就能实现⾃动加载,之前写了⼀篇可以⾃动加载springboot静态⽂件的,这次的只需要在原来的基础上再加⼀些配置即可实现springboot⼯程的热部署,步骤如下:
1、pom⽂件增加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork> <!--重要-->
</configuration>
</plugin>
</plugins>
</build>
2、yml⽂件中添加配置使其⽣效:spring framework是什么框架的
# devtools
debug: true
spring:
devtools:
restart:
enabled: true #设置开启热部署
freemarker:
cache: false #页⾯不加载缓存,修改即时⽣效
3、快捷键:Ctrl+Alt+S
4、快捷键:Ctrl+Shift+A,输⼊Registry,点击进⼊勾选:
以上这篇解决SpringBoot使⽤devtools导致的类型转换异常问题就是⼩编分享给⼤家的全部内容了,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论