解决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.boot.devtools.restart.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 contain restart.exclude. 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文件,文件加上类似下面的配置:

restart.exclude.companycommonlibs=/mycorp-common-[\w-]+.jar restart.include.projectcommon=/mycorp-myproj-[\w-]+.jar

All property keys must be unique. As long as a property starts with restart.include. or restart.exclude. 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>com.example</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(PortalApplication.class.getClassLoader());
    //这里对象引用是Object类型
    System.out.println(deserialize);
    System.out.println(deserialize.getClass().getClassLoader());
    context.getBeanFactory().destroySingletons();
  }
}

如上,是不会报错的,因为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(PortalApplication.class.getClassLoader());
    //注意这里进行了一次类型强转
    System.out.println((DemoBean)deserialize);
    System.out.println(deserialize.getClass().getClassLoader());
    context.getBeanFactory().destroySingletons();
  }
  //...

结果是会抛出:

Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.boot.devtools.restart.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.boot.devtools.restart.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.boot.devtools.restart.classloader.RestartClassLoader@1d9fbdd4 DemoBean{age=null, name='null'} org.springframework.boot.devtools.restart.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文件中添加配置使其生效:

# devtools
debug: true
spring:
 devtools:
  restart:
   enabled: true #设置开启热部署
 freemarker:
  cache: false  #页面不加载缓存,修改即时生效

3、快捷键:Ctrl+Alt+S

4、快捷键:Ctrl+Shift+A,输入Registry,点击进入勾选:

以上这篇解决SpringBoot使用devtools导致的类型转换异常问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • spring boot项目导入依赖后代码报错问题的解决方法

    代码截图如图所示(由于本人问题已经解决,没来得及截图,所以在网上找了一张图片) ​ 针对图中所示的情况,可参考一下解决方案: 方案一: 在 Idea 导入 Spring Boot 项目代码报红,试过更改maven配置,maven clean操作,执行-U idea:idea等命令还是提示:cannot resolve symbol 'SpringBootApplication' .我最终解决方法是导入要导入项目的pom.xml文件,而不是导入现有项目解决.选择pom.xml后会弹出提示框,选择a

  • 项目依赖Springboot jar失败解决方案

    1.原因 因为springboot-maven-plugin打包的第一级目录为Boot-INF,无法引用 2.解决 不能使用springboot项目自带的打包插件进行打包 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </p

  • Springboot跨域问题三种解决方案

    使用vue+axios+spring boot前后端分离项目时会出现跨域问题 解决方式: 一: 全局配置 /** * 就是注册的过程,注册Cors协议的内容. * 如: Cors协议支持哪些请求URL,支持哪些请求类型,请求时处理的超时时长是什么等. */ @Override public void addCorsMappings(CorsRegistry registry) { registry .addMapping("/**")// 所有的当前站点的请求地址,都支持跨域访问. .

  • 解决SpringBoot使用devtools导致的类型转换异常问题

    问题: 最近在使用新框架SpringBoot + shiro + spring-data-jpa时,为了体验下spring自带的热部署工具的便捷,于是引入了 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <!-- optional=true,依赖不会传递,该项目依赖devtools:

  • 解决java.lang.ClassCastException的java类型转换异常的问题

    在项目中,需要使用XStream将xml string转成相应的对象,却报出了java.lang.ClassCastException: com.model.test cannot be cast to com.model.test的错误. 原因: 项目中应该是采用了热部署,devtools,因为累加载器的不同所以会导致类型转换失败 措施: 在pom.xml中将以下代码注释掉: <dependency> <groupId>org.springframework.boot</g

  • 解决SpringBoot集成Eureka导致返回结果由json变为xml的问题

    SpringBoot集成Eureka导致返回结果由json变为xml 解决方案 在请求的Mapping上加上 produces = { "application/json;charset=UTF-8" } 例如: @GetMapping(value = "/user-instance", produces = { "application/json;charset=UTF-8" }) 以下是json和xml @GetMapping(value =

  • 解决Springboot 2 的@RequestParam接收数组异常问题

    目录 Springboot 2 的@RequestParam接收数组异常 所以这里给出解决方式: Springboot 的 用数组接参方法 Post接参 RequestParam 其中value 的值随传参改变 有几点需要注意: Springboot 2 的@RequestParam接收数组异常 最近Vue 开发前端,然后向后台springboot 2 传递数组,发现springboot 2 接收数组方式无法使用 -- @RequestParam("ids[]") List<St

  • SpringBoot集成Swagger2实现Restful(类型转换错误解决办法)

    pom.xml增加依赖包 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <

  • Kotlin遍历集合导致并发修改异常的原因和解决方法

    各位android 老司机们,对于android 遍历结合的时候,发生并发修改异常一定毫不陌生: 之前看到过一篇文章, 在阿里巴巴Java开发手册中,有这样一条规定: 其实,增强for循环也是Java给我们提供的一个语法糖,如果将以上代码编译后的class文件进行反编译(使用jad工具)的话,可以得到以下代码: 1.原因:(其实我都不想在各位老司机面前再赘述这个了.-_-||) 这个异常产生的原因是,迭代器依赖于集合而存在,在判断成功后,集合中添加了新的元素,而迭代器并不知道,所有就报错了.其实

  • Mysql 字符集不一致导致连表异常的解决

    目录 1. 解决方法 2. mysql字符集 字符集 校验规则 做一个简单的如下的连表查询,居然直接提示错误,居然是字符集不一致的问题,本文记录一下mysql的字符集类型,以及下面这个问题的解决方案 select a.id, b.id from tt as a, t2 as b where a.xx = b.xx -- Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_general_ci,IMPLICIT)

  • springboot集成shiro遭遇自定义filter异常的解决

    目录 springboot集成shiro遭遇自定义filter异常 1.用maven添加shiro 2.配置shiro 3.实现自定义的Realm.filter.SubjectFactory等 4.重点记录filter配置中出现的问题 5.解决方案 shiro自定义异常无效 springboot集成shiro遭遇自定义filter异常 首先简述springboot使用maven集成shiro 1.用maven添加shiro <!--shiro--> <dependency> <

  • SpringBoot如何优雅的处理全局异常

    前言 本篇文章主要介绍的是SpringBoot项目进行全局异常的处理. SpringBoot全局异常准备 说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码. 开发准备 环境要求 JDK:1.8 SpringBoot:1.5.17.RELEASE 首先还是Maven的相关依赖: <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.

  • 解决SpringBoot多模块发布时99%的问题

    解决SpringBoot多模块发布时99%的问题?SpringBoot发布的8个原则和4个问题的解决方案 如果使用 SpringBoot 多模块发布到外部 Tomcat,可能会遇到各种各样的问题.本文归纳了以下 8 个原则和发布时经常出现的 4 个问题的解决方案,掌握了这些原则和解决方案,几乎可以解决绝大数 SpringBoot 发布问题. SpringBoot 多模块发布的 8 大原则 1 在发布模块打包,而不是父模块上打包 比如,以下项目目录: 如果要发布 api 就直接在它的模块上打包,而

随机推荐