
最近在升级3.3版本cglib包后领料出库单启用DEP报错。
net.sf.cglib.asm.$MethodTooLargeException: Method too large

DEP使用的是第三方开源包cglib实现方法前置脚本和后置脚本的执行,这里报错有个比较明显的提示
Method too large。
直译过来就是方法太大了,是哪个方法太大,也在异常下面给出了:getIndex。
我们都知道java文件会编译成class文件,方法体会编译成具体的指令。在java中规定了指令的最大条数,这个数字是用4个字节去存储所以,最多是65535条。
难道这个getIndex方法编译之后的指令已经超过了这个限制?
去代码中找这个方法,果然找不到,因为如果代码中有这么大的方法,编译器会直接报错的,不会到运行时才报错,很明显这个方法是cglib动态代理类中自动生成的。
查看cglib相关文档,在虚拟机参数中设置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY参数,可以将动态代理类输出到指定的路径。
在代码中或者在虚拟机参数中按如下设置。
代码:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:/cglibgenClass");
虚拟机参数:
EAS客户端在eas/client/bin/client.vmoptions文件中制定虚拟机参数。

当然就算设置了这个参数,也生成不了代理类,因为在生成的时候就会报错。
所以我们只能自己写个测试类测试一下。
测试类如下:
被代理类,有两个方法
package com.ice.yu;
public class KLen {
public void a1(){System.out.println("longMethodName");}
protected void a2(){System.out.println("longMethodName");}
}
通过cglib代理一下
package com.ice.yu;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class MainClass {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:/cglibgenClass");
KLenProxy proxy = new KLenProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(KLen.class);
enhancer.setCallback(proxy);
enhancer.setUseFactory(true);
KLen len = (KLen)enhancer.create();
System.out.println(len.getClass().getDeclaredMethods().length);
try {
len.a1();
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
运行后可以看到我们指定的位置已经产生了代理类文件

通过反编译工具反编译查看(这里推荐使用luyten,jd-gui有时候反编译会出问题,反编译不出来)

确实这个代理类生成了getindex方法,并且通过switch case 的方法把我们的a1和a2方法都生成了对应的分支,理论上如果我们的类中方法很多的时候,确实有可能把这个方法撑爆。(这里我测试了下方法名很简单的情况下,大概2000多个方法会触发这个Method too large报错。)
但是我们的领料出库单好像没这么多方法呀!只能调试看看了。调试了下,发现cglib代理一个类的时候不仅会代理本身的public和protected方法,还会代理父类的方法。因为EAS单据的继承体系比较深,并且一个类中方法很多,加在一起确实会有几千个。
但是很奇怪的是,在升级3.3版本cglib包之前是不会报错的,之前用的是2.2版本,比较古老。有对比了下两个版本代理方法的数量,发现2.2版本代理的方法,会比3.3少600多个,对比两个版本代理方法的区别,发现3.3会比2.2多代理一些protected方法。
分析源码发现两个版本有一处判断的差异,导致了现在这个现象。
看下面的这个方法,这是筛选代理方法的过滤,有个默认参数protectedOK,此参数默认值为false,字面理解就是要不要代理protected方法。2.2版本如果判断方法为protected,那直接返回protectedOK,那protected就不会被代理。
但是3.3中,如果判断方法为protected并且protectedOK为false时,还会判断方法所在类和代理类是否在相同包下,即和代理类相同包名下的类中,即使是protected方法也会被代理。

查看git上的修改记录

此处为3.2版本的修改,相同包下的protected方法可以被代理。
到此时问题已经很明显了,由于3.3版本会比之前版本多代理600多的protected方法,所以导致代理类的getIndex方法体太大,超过了java的限制。最终是通过降级第三方包为3.1版本去处理的,但是这里还是有隐患,如果我们后续再在类中添加很多的public方法,当超过一定数量时,通过cglib代理还是会产生报错。
还得是海洋大佬