• easyexcel和poi版本冲突报错深入解析v2


    easyexcel报错解决

    问题

    项目由poi改用easyexcel,报错如下: java.lang.NoSuchMethodError: ‘org.apache.poi.ss.usermodel.CellType
    org.apache.poi.ss.usermodel.Cell.getCellType()’

    原因

    easyexcel中的poi和项目原本的poi版本冲突问题。 由于之前做过easyexcel项目,就把所以子工程pom里的poi注释掉了。
    关键:忽略了parent项目pom的dependencyManagement中版本锁定的poi,这里误以为在子工程未使用就不会冲突。

    解决

    将项目所有有关poi的dependency全部注释掉,包括dependencies和dependencyManagement。
    推荐:使用快捷键ctrl+shift+f直接搜索poi,找到直接注释

    上述只是问题的解决方案,并没有系统的介绍为什么会报错。

    1.问题

    1、报的什么错?NoSuchMethodError 方法不存在错误
    2、发生在编译期,还是运行时?
    3、如果发生在运行时,为什么编译的时候没有识别出来这个方法不存在?

    那么通过下面的解析,会清楚的理解上述问题。

    2.模拟错误

    由于easyexcel源码不方便修改,所以这里使用自己代码实现(代码无实际意义),主要演示报错。

    2.1创建2个maven项目

    easye模拟easyexcel,poi模拟poi。
    在这里插入图片描述
    自己项目调用了easyexcel的方法,而easyexcel又调用了poi方法。所以模拟,需要在poi创建一个getType方法。easye里创建testGet方法调用getType方法。

    具体代码如下:
    poi的pom:初始版本设置为1.0,版本后面会更改

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>cn.zxhgroupId>
            <artifactId>test01artifactId>
            <version>1.0version>
        parent>
    
        <groupId>cn.zxhgroupId>
        <artifactId>poiartifactId>
        <version>1.0version>
    
        <properties>
            <maven.compiler.source>8maven.compiler.source>
            <maven.compiler.target>8maven.compiler.target>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        properties>
    
    project>
    

    CellImpl.java

    package cn.zxh.poi;
    
    public class CellImpl implements Cell {
        @Override
        public int getType() {
            return 1;
        }
    }
    

    Cell.java

    package cn.zxh.poi;
    
    public interface Cell {
        public int getType();
    }
    

    easye的pom:初始版本设置为1.0,引用poi1.0版本库

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>cn.zxhgroupId>
            <artifactId>test01artifactId>
            <version>1.0version>
        parent>
        <groupId>cn.zxhgroupId>
        <artifactId>easyeartifactId>
        <version>1.0version>
        <properties>
            <maven.compiler.source>8maven.compiler.source>
            <maven.compiler.target>8maven.compiler.target>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        properties>
    
        <dependencies>
            <dependency>
                <groupId>cn.zxhgroupId>
                <artifactId>poiartifactId>
                <version>1.0version>
            dependency>
        dependencies>
    
    project>
    

    MyGet.java

    package cn.zxh.easye;
    
    import cn.zxh.poi.Cell;
    import cn.zxh.poi.CellImpl;
    import java.util.Objects;
    
    public class MyGet {
        public void testGet(){
            Cell cell = new CellImpl();
            //没有实际意义,主要是调用getType
            if(Objects.equals(cell.getType(),1)){
                System.out.println("pass");
            }
            System.out.println("fail");
        }
    }
    
    

    2.2安装到本地maven仓库

    点击安装
    在这里插入图片描述
    然后找到自己的maven仓库内容如下
    在这里插入图片描述

    在这里插入图片描述

    2.3在其他项目引入easye库

    注:找个别的工程(不要还在这个工程),因为模拟引入easyexcel。自己工程肯定和easyexcel不在一个工程下。
    在这里插入图片描述

    2.4创建测试类并模拟调用

    这里直接调用MyGet 的testGet。

    package com.zxh.project.test1;
    
    import cn.zxh.easye.MyGet;
    import cn.zxh.poi.Cell;
    
    public class MyTest {
        public static void main(String[] args) {
            MyGet myGet = new MyGet();
            myGet.testGet();
        }
    }
    
    

    这里先模拟版本一致正常情况,发现正常运行。
    在这里插入图片描述

    2.5降低poi的版本

    找到之前自己的poi项目,更改getType,这里把返回类型换成String。
    在这里插入图片描述
    在这里插入图片描述

    poi.pom里的版本改为0.5,然后只打包poi
    在这里插入图片描述

    在这里插入图片描述

    2.6使用降低poi0.5版本

    其他项目pom引入0.5版本
    在这里插入图片描述
    这里运行时会使用0.5,这就是常见的poi和easyexcel版本的冲突。
    在这里插入图片描述

    2.6错误模拟成功

    果然不出所料,运行时报错了,这里打了断点,编译肯定通过的,确实为运行时报错。
    在这里插入图片描述
    在这里插入图片描述

    2.7再再降低poi版本

    重复上部分,降低到0.1,直接把poi中的getType的方法删掉(方便理解),再打包,然后引入改为0.1

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    发现依然编译通过,运行时报错。

    3.原因分析

    那么有疑问,既然用的是低版本poi0.1库,这个方法都不存在,为什么还编译通过了呢。那么再做个实验。

    首先将easye1.0 和MyTest复制到另一个普通java项目。

    在这里插入图片描述
    在这里插入图片描述
    然后添加类库
    在这里插入图片描述

    然后这里也是运行时报错,只是错误变了,类不存在。因为压根就没有引入poi的库,只引入了easye,poi库中的类肯定不存在
    在这里插入图片描述
    然后这里引入的easye库(包括maven引入的),都是已经编译好的.class文件
    在这里插入图片描述

    4.总结

    那么我们就可以大胆的猜测了(为什么时猜测,我也不知道对不对,欢迎大家讨论)
    编译阶段,仅仅对直接引入的库中的方法进行检测是否存在,例如这里只判断easye库的MyGet.testGet,而easye库如果引用了其他库的方法,例如poi中的方法,由于已经编译成了MyGet.class,不再进行MyGet.class重新编译,所以深层的方法不在编译器发现(因为编译MyTest.class,只需要知道MyGet.testGet即可)。

    JVM运行时,会根据方法的签名进行调用,如果方法的签名不在,报错。
    下面为MyGet.testGet的class源码,
    其中invokeinterface #4 count 1
    这里就是获取cn/zxh/poi/Cell.getType : ()I 这种签名的方法。如果没有报错。

    2.6错误模拟成功

    这里报错是getType已经改成了String类型,I标识int,签名不一致找不到。

    2.7再再降低poi版本

    这里报错更简单,直接方法都删掉了,签名肯定找不到。

    3.原因分析

    这个里面的报错,是根据下面的源码
    0 new #2
    根据类路径找不到类,库都没有引入,所以报错。

     0 new #2 <cn/zxh/poi/CellImpl>
     3 dup
     4 invokespecial #3 <cn/zxh/poi/CellImpl.<init> : ()V>
     7 astore_1
     8 aload_1
     9 invokeinterface #4 <cn/zxh/poi/Cell.getType : ()I> count 1
    14 invokestatic #5 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
    17 iconst_1
    18 invokestatic #5 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
    21 invokestatic #6 <java/util/Objects.equals : (Ljava/lang/Object;Ljava/lang/Object;)Z>
    24 ifeq 35 (+11)
    27 getstatic #7 <java/lang/System.out : Ljava/io/PrintStream;>
    30 ldc #8 <pass>
    32 invokevirtual #9 <java/io/PrintStream.println : (Ljava/lang/String;)V>
    35 getstatic #7 <java/lang/System.out : Ljava/io/PrintStream;>
    38 ldc #10 <fail>
    40 invokevirtual #9 <java/io/PrintStream.println : (Ljava/lang/String;)V>
    43 return
    
  • 相关阅读:
    卓越领先!安全狗入选2023年福建省互联网综合实力50强
    K8s之CRD
    每日一题:2022.11.11最后的简单模拟题
    湖北省科技企业孵化器和众创空间申报奖励补贴标准,2022年认定条件汇总
    Vue项目使用axios配置请求拦截和响应拦截以及判断请求超时处理提示
    巧用 Java 8 的 Optional 优雅的规避 NPE
    文本格式清理工具 TextSoap mac中文版软件特色
    SOA和微服务是一回事吗
    PostgreSQL使用pgAdmin创建表后查询时提示“关系不存在”
    Go:基于BDD的测试框架 Ginkgo 简介及实践
  • 原文地址:https://blog.csdn.net/qq_38367575/article/details/139770484