我是靠谱客的博主 寂寞楼房,最近开发中收集的这篇文章主要介绍logger没有error 方法_探寻泛型方法ClassCastException元凶二、问题排查三、问题解决四、结论,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

b94617702808a6962ad1737dd092a6c3.png

f89776b5b49a960a620f942c02842c42.png


一、现象

本地开发环境在开发新功能的过程中突然出现了诡异的ClassCastException,之所以称之为诡异,是因为出现了对象强转自身所属类异常。 发生的场景:项目首次接入memcache,在通过泛型方法取值时,虽然取到的值和接收的值是同一个类,但是却出现强转异常。(自己无法强转自己 ⊙﹏⊙∥∣°) 抛出异常:java.lang.ClassCastException: com.zhqy.bean.Person cannot be cast to com.zhqy.bean.Person

环境如下:

macos mojave

idea 2018.2.1

springboot、memcache、spring-boot-devtools

二、问题排查

1、怀疑泛型方法有问题,但是方法简单到无法怀疑

// 代码功能简述:将memcache的对象反序列化成对应的实体类

// 简单代码如下

public <T> T get(String key) {

   try {

       return (T) mc.get(key);

   } catch (Exception e) {

       log.error(EXCEPTION, e);

   }

   return null;

}

2、怀疑是工具方法中的泛型有问题,导致无法正确转换 使用Junit和main方法运行具体的工具方法,发现结果一直是正确的,未出现一次ClassCastException。

三、问题解决

一般的排查手段已经无法解决问题,最后还是同事「张帆」提出,可能是因为spring-boot-devtools热部署功能使用了自定义ClassLoader导致的问题。

验证理论的过程如下

 1、先简单准备自定义classLoader的实例代码,代码如下:

// 调用方法:getPersonUseMyClassLoader

Person person =  test.getPersonUseMyClassLoader();

// 测试类中的getPersonUseMyClassLoader方法

// 当项目中使用了自定义CLassLoader的情况,一些泛型方法就会出问题

// 例如:项目中使用了spring-devtool,memcache的get方法如果用了泛型方法,就会出问题

// 此方法模拟memcache的get泛型方法

public <T> T getPersonUseMyClassLoader() {

   MyClassLoader loader = new MyClassLoader();

   Class> aClass = loader.findClass(Person.class.getName());

   try {

       Object obj = aClass.newInstance();

       return (T) obj;

   } catch (Exception e) {

       logger.error(String.format("错误:%s", e));

   }

   return null;

}

// MyClassLoader类

public class MyClassLoader extends ClassLoader {

   private Logger logger = Logger.getLogger(MyClassLoader.class);

   @Override

   public Class> findClass(String name) {

       String myPath =  "file://" + MyClassLoader.class.getResource("/").getPath().replace("test-classes", "classes") + name.replaceAll("\.", "/") +".class";

       logger.debug(String.format("class file path:%s", myPath));

       byte[] cLassBytes = null;

       Path path = null;

       try {

           path = Paths.get(new URI(myPath));

           cLassBytes = Files.readAllBytes(path);

           return defineClass(name, cLassBytes, 0, cLassBytes.length);

       } catch (IOException | URISyntaxException e) {

           logger.error(String.format("错误:%s", e.getMessage()));

       }

       return null;

   }

}

2、根据抛出的强转异常,将示例代码的第一句进行如下修改:

Person person =  test.getPersonUseMyClassLoader();

改为

Object obj =  test.getPersonUseMyClassLoader();

logger.info(String.format("obj.equals(Person.class):%s", obj.getClass().equals(Person.class)));

// 输出false

logger.info(String.format("obj instanceof Person:%s", obj instanceof Person));

// 输出false

logger.info(String.format("obj 的 classLoader:%s", obj.getClass().getClassLoader()));

// 输出com.zhqy.classLoad.MyClassLoader

logger.info(String.format("Person 的 classLoader:%s", Person.class.getClassLoader()));

// 输出sun.misc.Launcher$AppClassLoader

根据修改后obj instanceof Person的结果可知,obj不是Person的对象。(很蛋疼的结果,因为理论上obj确实是Person类的对象) 在这种情况下,分析示例代码的字节码得知,instanceof结果为false和泛型方法出现强转异常,是因为泛型方法仅仅只是省去人为指定强转类型而已,并不是没有强转的那一步。详见字节码文件内容:

// getPersonUseMyClassLoader() 方法返回一个 Object 类型的对象

INVOKEVIRTUAL com/zhqy/bean/Person.getPersonUseMyClassLoader ()Ljava/lang/Object;

// 检查是否可以转换成Person对象,如果不可以转换则会抛出异常

CHECKCAST com/zhqy/bean/Person

四、结论

在示例代码中,虽然被强转对象和接收的对象是同一个,但是因为classLoader不是同一个,在CHECKCAST时就会抛出ClassCastException异常。 解决办法

1、创建META-INF/spring-devtools.properties文件

2、将memcache也配置为使用spring-boot-devtools的ClassLoader

#spring-devtools.properties文件示例

restart.include.memcachedclient=/com.zhqy.spat.memcachedclient-.*jar

参考文档: INSTANCEOF、CHECKCAST关键字解释 [张振阳] spring-boot-devtools 不同ClassLoader引起的问题 spring-boot-devtools文档,Customizing the Restart Classloader部分

(完)

推荐阅读

  • Java面试通关要点 汇总集【最终版】

  • 如何合理的设计代码分层

  • 微服务初级设计指南

  • 良好的 API 设计指南

  • HTTPS 项目实战指南

  • 常用性能监控指南

  • 保证服务4个9的可用性的核心思路

  • 一次应用 CPU 飙高的血案排查过程

  • 探寻 Redis 超时元凶的全过程剖析

  • 探寻 Redis 内存诡异增长的元凶

  • 如何健壮你的后端服务

  • 消息中间件选型分析

3c3e9313bd202c6639b32394d1715678.png

更多精彩文章,尽在「服务端思维」!

903fb4b9a7afd297a3c5e281273a9c2c.png

最后

以上就是寂寞楼房为你收集整理的logger没有error 方法_探寻泛型方法ClassCastException元凶二、问题排查三、问题解决四、结论的全部内容,希望文章能够帮你解决logger没有error 方法_探寻泛型方法ClassCastException元凶二、问题排查三、问题解决四、结论所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部