运行期间抛出NoSuchMethodError复现及原因分析

完整工程示例见:no_such_method

背景

在开发中常遇到抛出NoSuchMethodError的情景,但想不通找不到方法的问题为什么可以通过编译阶段,所以创建了三个工程来模拟出NoSuchMethod的场景进行研究。

模拟NoSuchMethodError

这里简单的复现了NoSuchMethodError出现的场景:outer有1.0和2.0两个不兼容的版本,caller依赖于outer的2.0版本,而main中又显式指出了outer的1.0版本,执行main中的主方法即报错。

下面是main的依赖树:

[INFO] com.example:main:jar:1.0-SNAPSHOT
[INFO] +- com.example:caller:jar:1.0:compile
[INFO] \- com.example:outer:jar:1.0:compile

步骤

1. 生成1.0版本的outer

1.1 创建Painter类,其中包含一个接收String类型的print方法。

package org.example;

public class Painter {

    // 1.0
    public static void print(String name) {
        System.out.println("Hello " + name);
    }
}

1.2 设置pom中的version为1.0

1.3 运行mvn clean install安装到本地仓库

2. 生成2.0版本的outer

2.1 更新Painter类,更新print方法,改为接收一个Integer类型的参数。

package org.example;

public class Painter {

    // 1.0
    //public static void print(String name) {
    //    System.out.println("Hello " + name);
    //}

    // 2.0
    public static void print(Integer i) {
        System.out.println("Hello NO." + i);
    }
}

2.2 设置pom中的version为2.0

2.3 运行mvn clean install安装到本地仓库

3. 创建caller

3.1 caller依赖于2.0版本的outer,pom片段如下

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>outer</artifactId>
            <version>2.0</version>
        </dependency>
    </dependencies>

3.2 创建Caller类,调用2.0版本outer中的Painter

package com.example;

import org.example.Painter;

public class Caller {
    public static void call() {
        Painter.print(1);
    }
}

3.3 运行mvn clean install安装到本地仓库

4. 创建main

4.1 设置pom依赖如下

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>caller</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>com.example</groupId>
            <artifactId>outer</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>

4.2 新建Main类,其中有main方法来调用Caller中的静态方法

package com.example;

public class Main {
    public static void main(String[] args) {
        Caller.call();
    }
}

4.3 编译mvn compile -X,我们可以看到最终使用的outer版本为1.0,并且并没有报错

[DEBUG] com.example:main:jar:1.0-SNAPSHOT
[DEBUG]    com.example:caller:jar:1.0:compile
[DEBUG]    com.example:outer:jar:1.0:compile

[INFO] BUILD SUCCESS

4.4 运行main中的主函数,抛出NoSuchMethodError,命令为mvn exec:java -Dexec.mainClass="com.example.Main" -X,结果如下

java.lang.NoSuchMethodError: org.example.Painter.print(Ljava/lang/Integer;)V
    at com.example.Caller.call (Caller.java:7)
    at com.example.Main.main (Main.java:5)
    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.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:282)
    at java.lang.Thread.run (Thread.java:748)

原因分析

为什么编译时不报找不到方法呢?

因为依赖的jar已经编译过,无需再编译,所以不报错。或者说,编译过程中只会检查当前java文件的正确性,不会关心依赖的class的正确性。而在运行时则动态加载类,才发现找不到方法,故抛出NoSuchMethodError。

验证

将Main反编译,使用命令javap -c com.example.Main,结果如下

Compiled from "Main.java"
public class com.example.Main {
  public com.example.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #2                  // Method com/example/Caller.call:()V
       3: return
}

可以看到main的编译过程中并没有涉及到对outer工程中Painter#paint方法的引用,所以不会在编译期间发现这个问题。

而如果使用1.0版本的outer作为caller的依赖,则在编译时就会报异常,编译命令:mvn clean compile,异常片段如下

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project caller: Compilation failure
[ERROR] /no_such_method/caller/src/main/java/com/example/Caller.java:[7,23] 不兼容的类型: int无法转换为java.lang.String
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容