美文网首页
使用cglib中BeanCopier遇到的问题

使用cglib中BeanCopier遇到的问题

作者: FuriousPws002 | 来源:发表于2020-01-05 22:47 被阅读0次

问题描述

spring boot项目,idea开发环境使用BeanCopier的copy方法没有任何问题
使用maven打包成jar包后,用java -jar 命令执行,提示java.lang.VerifyError: Bad type on operand stack错误
新建TestMain类,单独测试copy方法同样提示错误
详细错误如下

Exception in thread "main" java.lang.IllegalStateException: Unable to load cache item
        at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79)
        at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
        at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
        at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
        at net.sf.cglib.beans.BeanCopier$Generator.create(BeanCopier.java:95)
        at net.sf.cglib.beans.BeanCopier.create(BeanCopier.java:51)
        at TestMain.<init>(TestMain.java:8)
        at TestMain.main(TestMain.java:13)
Caused by: java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
TestMain$UserDO$$BeanCopierByCGLIB$$eb78a326.copy(Ljava/lang/Object;Ljava/lang/Object;Lnet/sf/cglib/core/Converter;)V @12: invokevirtual
  Reason:
    Type 'java/lang/Object' (current frame, stack[3]) is not assignable to 'java/lang/Long'
  Current Frame:
    bci: @12
    flags: { }
    locals: { 'TestMain$UserDO$$BeanCopierByCGLIB$$eb78a326', 'java/lang/Object', 'java/lang/Object', 'net/sf/cglib/core/Converter' }
    stack: { 'TestMain$UserDO', 'TestMain$UserVO', 'TestMain$UserDO', 'java/lang/Object' }
  Bytecode:
    0x0000000: 2cc0 000d 2bc0 000f 5cb6 0015 b600 195c
    0x0000010: b600 1db6 0021 b1
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Unknown Source)
        at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:467)
        at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
        at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
        at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
        at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
        at java.util.concurrent.FutureTask.run(Unknown Source)
        at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
        ... 7 more

执行环境

-jdk 1.8.0_171
-cglib 3.3.0
-asm 7.2
-lombok 1.18.10
-Idea 2019.3
TestMain中示例代码如下

import lombok.Data;
import lombok.Getter;
import net.sf.cglib.beans.BeanCopier;

public class TestMain {

    @Getter
    private final BeanCopier dto2do = BeanCopier.create(UserVO.class, UserDO.class, false);
    @Getter
    private final BeanCopier do2dto = BeanCopier.create(UserDO.class, UserVO.class, false);

    public static void main(String[] args) {
        TestMain testMain = new TestMain();
        UserVO userVO = new UserVO();
        userVO.setId(1L);
        userVO.setUsername("name");
        System.err.println(testMain.dto2do(userVO));
    }

    UserDO dto2do(UserVO userVO) {
        UserDO userDO = new UserDO();
        this.getDto2do().copy(userVO, userDO, null);
        return userDO;
    }

    UserVO do2dto(UserDO userDO) {
        UserVO userVO = new UserVO();
        this.getDo2dto().copy(userDO, userVO, null);
        return userVO;
    }

    @Data
    private static abstract class Entity<ID> {
        private ID id;
    }

    @Data
    private static class UserVO extends Entity<Long> {
        private String username;
    }

    @Data
    private static class UserDO {
        private Long id;
        private String username;
    }
}

问题分析

spring boot项目采用idea开发环境没有任何问题,可以成功执行,而用maven打包后,用原生的java -jar命令执行就提示上面的错误信息,进一步用测试用例TestMain单独测试同样出现如上错误。初步分析,在spring boot中idea的启动命令和单独的TestMain中main方法的启动命令以及java -jar命令的执行环境不完全相同,到底哪儿不同呢?
查看idea控制台
spring boot的启动命令如下

"C:\Program Files\Java\jdk1.8.0_171\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:7587,suspend=y,server=n -javaagent:C:\Users\Furious\.IntelliJIdea2019.3\system\captureAgent\debugger-agent.jar -noverify -Dfile.encoding=UTF-8 -classpath ...

TestMain的启动命令如下

"C:\Program Files\Java\jdk1.8.0_171\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:7587,suspend=y,server=n -javaagent:C:\Users\Furious\.IntelliJIdea2019.3\system\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath ...

而java -jar命令只是单独的jar -jar springboot.jar
对比spring boot和TestMain的启动命令,很容易发现spring boot命令中多了-noverify参数,是不是这个参数在起作用呢?
执行java -jar -noverify springboot.jar 发现的确可以成功执行,ok,问题就出在noverify这个参数上。那是不是说以后使用BeanCopier出现这个问题时,就加noverify这个参数就万事大吉了呢?
先看了一下jvm参数中对verify的解释
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
意思是jvm虚拟机会对class文件的字节码进行校验,同时也可以配置成不校验字节码,但是,文档中明确说了,不推荐关闭字节码的校验
原文引用

Do not turn off verification as this reduces the protection provided by Java and could cause problems due to ill-formed class files.

那怎么办?同时,又搜到一篇博客,更加证实了不能随意使用noverify参数,点我查看博客

既然是字节码校验失败,字节码的来源是类文件,而类是我们自己定义的,那么问题应该就在类的定义里面,what?类定义还能出岔子?
初步观察UserDO,UserVO类,无非是UserVO是继承了Entity类,莫非是继承使得字节码校验失败,导致不能复制?
那么让UserVO不去继承Entity类试试勒,果然,不继承Entity类,完全没有任何问题,只不过没有id属性而已,但是继承本身就是java多态的一种体现,不应该出现因为继承而导致字节码校验失败啊。。。
再返回去看错误的详细信息,发现了新大陆

Type 'java/lang/Object' (current frame, stack[3]) is not assignable to 'java/lang/Long'

object不能分配long类型?一万头羊驼飘过。
不是指定了泛型ID的类型为long么,既然这样,那Entity类不用泛型勒
改成如下形式

    private static abstract class Entity {
        private Long id;
    }

再启动,完全没有问题,此时,找到问题根源所在。但是,缓过神来,Entity中ID为泛型,本身就是业务所需,也完全没有任何问题啊,不能强制人家固定id的属性就为Long啊。
此时,再回过头去看BeanCopier中copy的方法,发现第三个参数为Converter,既然支持类型转换,试试看,dto2do和dto2to方法分别换成如下形式

    @Getter
    private final BeanCopier dto2do = BeanCopier.create(UserVO.class, UserDO.class, true);

    UserDO dto2do(UserVO userVo) {
        UserDO userDO = new UserDO();
        this.getDto2do().copy(userVo, userDO, (value,clazz,setter) -> value);
        return userDO;
    }

运行TestMain的main方法,完全没有问题。
咦,为什么do2dto没有提示字节码错误勒,对比dto2do和do2dto成员变量,发现一个泛型是源对象,而另外一个则是目标对象,得出结论,只有源类继承了泛型类的时候,才会出现泛型丢失的情况。目标对象为泛型时,没有问题。

总结

当源类继承了泛型类的时候,使用BeanCopier的copy方法需要显示的设置Coverter转换器,否则,字节码校验不通过。

相关文章

网友评论

      本文标题:使用cglib中BeanCopier遇到的问题

      本文链接:https://www.haomeiwen.com/subject/hgqtactx.html