总结一次类型转换异常的解决

2,094 阅读6分钟
      先说下本人是1年外包狗,正在学习的路上摸爬滚打。周五在和其他系统进行联调的时候,人家反馈说一调用我的服务流程就异常中断了。我这里的应用日志一直开着,也没看见请求进来,突然灵光一闪我怎么忘记了服务器日志,打开一看,果然报错了,错误描述:java.lang.ClassCastException。再具体一看报错的地方,这是好几年前就写好且在生产上运行着的组件啊,为什么突然就出现一个类型转换错误呢?

       要解决这个问题,首先说下我这的系统架构,如下图。


调用流程就是请求进来服务器到应用2,通过一个写好的组件C将请求发送到子系统进行处理。有人肯定会问应用1是干嘛的,它仅仅只是用来启动子系统的,没有其他卵用,子系统通过应用1来挂载到服务器上。应用2通过JNDI来调用子系统中的服务,此时就要通过

CProxy c=(CProxy)ctx.lookup(jndiname)来进行强转了。CProxy就是组件C中的一个类,主要用来进行数据交换的。然后就是这个地方,出现了java.lang.ClassCastException。说实话,看到这个错,我很茫然,啥玩意儿,生产上不是跑着的?你现在告诉我测试环境出了问题。顿时一阵凉凉,我不会把哪个地方给搞错了吧,擦。于是我赶紧把测试版本回退到了一个月前,调用老的服务,还是报错了,此时的我是懵逼的,弱小无助。

     这块工作内容除了我还有一位公司的多年老开发也很熟,于是我就去问他了,他负责这块业务很多年,也有一些经验。然而结果让人失望,他也不会,他给的那些排查方案我一看就知道方向不对,算了,这个坑看来还是得自己填。于是乎,我就在网上开始了我的寻找答案之旅。由于这块技术太过老旧,资源也少,周五找了一晚上也没什么收获,一开始就觉得可能是服务器升级打补丁造成的,因为生产在运行给了我一个错觉。一夜没有什么进展,周六我又去公司进行测试排查问题出现的原因。首先检查了服务器文件,检查了子系统文件,发现除了补丁包以外最后修改时间都是3年前的了。强转异常出现在应用2中,JNDI服务提供者是子系统,于是我去拿着相同的代码去子系统测试,没有报错。那问题肯定就是在应用2中了,可是应用2也只是用来与其他系统进行通讯用,几年没动过了,陷入僵局。周日继续加班,这里说下类型转换异常在什么情况下会发生,第一:父类引用指向的对象的类型不是子类的时候将产生java.lang.ClassCastException异常,这个大家应该都懂;第二:类型是它的类型,但是为什么也会报错,因为ClassLoader不一致,也会导致java.lang.ClassCastException异常。在JVM中,类只会被加载一次,类的双亲委派模型中类的加载过程这里就不细讲了,反正大概就行检查一下有没有被加载,没有被加载就给委派父类加载器去加载,父类加载器加载不了才会给子类加载器加载, 贴个找的图

                                    

我在应用2中将CProxylookup()所产生的对象的类加载器给打印出来了,发现他们是不一样的,CProxy用的是SystemClassloader,而lookup()产生的居然是一个自定义的类加载器。另外我在子系统中也把他们的classloader打印了出来,都是用自定义类加载器加载的,而这个自定义类加载器就是子系统的。报错的原因是已经找到了,但是我又开始疑惑了,组件C是写在服务器的启动脚本中定义的CLASSPATH环境变量中的,如果遵循双亲委派模型的规则,都应该是由SystemClassloader加载的才对。看来得翻子系统的源代码了,于是乎又去把子系统的启动类反编译了来看下,发现它实现了自己的类加载器去加载指定路径的所有jar,重点来了,它违背了双亲委派模型的原则,不再是先交由父加载器去加载,而是反转了过来,优先使用自己的自定义类加载器去加载指定路径的jar,如果加载不到,就会委派父加载器去加载。所以报错的现象就可以解释了,为什么同一个类却产生了不同的类加载器。那么如何解决呢,也很简单,只需要把组件C从子系统的加载路径下移除,重新找个位置放就可以了,再改下脚本中CLASSPATH加载此组件的路径。

      其实我现在也不清楚生产这几年是怎么过来的,按道理来讲应该会报错,公司领导说老交易其实已经没有交易量了,但是至少当时投产部署,测试环境测试过吧,难道没有出现问题?我现在也很疑惑,以至于现在虽然没有报错了,但也怀疑自己是不是还有什么地方没有关注到。

最后再贴几篇我觉得受益的classloader相关的文章。

1.查看JVM加载的类及类加载器的方法

www.cnblogs.com/z00377750/p…

2.JNDI 详解

wenku.baidu.com/view/81e687…

3.深入浅出ClassLoader

ifeve.com/classloader…

4.ClassLoader

www.cnblogs.com/549294286/p…

--- 2019.8.31  ps:

上线后的感悟。

作为一个小白,其实在工作中很多比较重要的任务上级都不会派给你,需要自己去不断深挖学习技术或是业务相关的ererything,这样才会在关键时刻凸显自己的作用。这个问题解决后我特地去让运维帮我核查了生产相关的配置,发现是正确的。从这件事也反映出工作中版本管理、环境管理的重要性,否则就会出现很多奇奇怪怪的问题,就像这次上线一样,测试环境没问题,生产上报错,排查到后半夜发现是测试与生产jdk不一致导致,生产多了一个jar,导致了冲突。