由一个NoSuchMethodError报错引发的思考

12 篇文章 0 订阅
10 篇文章 0 订阅

前言

前几天在dev环境遇到一个问题,一个同事在原有的数据库映射表的实体类中新增一个属性,在service中调用了这个属性的get方法,接口调用的时候报错get方法找不到,但代码中明明有此方法,同时在本地访问又是正常的,当时就怀疑是环境问题,对比一下本地的java环境和linux服务器上的环境,没有发现问题,于是开始面向搜索来寻找解决办法,在网上看到这样一篇博文《java.lang.NoSuchMethodError异常可能出现的原因》,按照步骤一一排查,还是没有找到问题所在,直到第二天,突然想到会不会有另一个同名类确实没有这个属性的get/set方法,全局搜索一下,果然,这么顺利找到这个不同工程下的同名包同名类,还“得益于”这个项目的模块结构,不然依赖包下的依赖包,多层依赖下要找到这个同名类可就没那么好找了。

项目结构

前面说了多亏了项目的结构才能顺利找到这个同名类,已经无力吐槽了(写的都是啥呀,**********),直接上图
在这里插入图片描述
目标代码逻辑在工程1中,同名类在工程4中,工程1的pom.xml引用了工程2的依赖包,工程2又引用了3中的两个jar依赖,3中两个子工程且都引用的工程4的jar包,按功能模块来看,2、3、4这个三个子工程可看成一个整体,原本同名类倒是没多大关系,可偏偏这两个同名类的包名也一样,而新增的属性只在目标类中有,同名类没有;
在这里插入图片描述
情况就是这样,有一个关键点我们先来思考一下。

类加载

我们先来了解一下类加载过程
在这里插入图片描述
(图片来源:类的加载过程

类加载过程
1、JVM会先去方法区中找有没有类对应的.class存在,如果有,就直接使用;如果没有,就把对应类的.class加载到方法区;
2、将.class加载到方法区的时候,分为两部分,首先将非静态内容加载到方法区的非静态区域内;
3、再将静态内容加载到方法区的静态区域内,加载完成后,对所有的静态成员变量进行默认初始化;
4、所有静态成员变量默认初始化后,进行显示赋值;再执行静态代码块;(执行静态代码块和静态成员变量显示赋值无先后顺序,与代码顺序有关,谁在前面先执行谁)
5、如果有继承关系,加载子类的时候会先加载父类,再加载子类。

1、JVM初始化步骤 ?
(1)如果这个类还没有被加载和连接,则程序先加载并连接该类
(2)如果这个类的直接父类还没有被初始化,则先初始化其直接父类
(3)假如类中有初始化语句。则系统依次执行这些初始化语句

2、类初始化时机 ?
只有对类的主动使用的时候才会进行类的初始化
类的主动使用包括以下6种:

  • 创建类的实例(new Object)
  • 访问某个类或接口的静态变量,或者对该静态变量赋值(static关键词)
  • 调用类的静态方法
  • 反射(如Class.forName(“com.demo.Test”))
  • 初始化某个类的子类,则其父类也会被初始化
  • Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类

3、同包同名的类的时候,使用哪个,哪个优先 ?
为什么本地执行没有问题,打成jar在服务器上的tomcat中调用却报错?
搞清这个问题,就能明白为什么会出现上面说的现象。

经过测试发现,本地执行的时候的确是使用的目标类,在eclipse运行的时候会根据jar的order顺序来使用类,而在tomcat下则是按照字母的顺序加载JAR文件。有了这个类以后,后面的同名类则不会加载了,正是因为这个原因导致了本地和dev环境的不同执行结果。

最后考虑到同名类的使用范围要远小于目标类,于是选择变更同名类的类名,这样问题到这里就解决了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值