引入JaCoCo导致的类型转换问题分析

01 问题描述
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将 JaCoCo是一款被广泛应用于公司内部的开源覆盖率工具,将其引用至测试环境后,机器启动正常,但在操作下单时出现异常,阻塞下单流程。
去除JaCoCo配置、重新编译和部署后下单功能恢复正常。堆栈信息显示,问题源于系统对请求字段进行加密时出现异常,因为无法完成类型转换抛出异常,“[Z cannot be cast to [Ljava.lang.Object”,从而阻塞下单流程。
以下为报错堆栈信息: java.lang.ClassCastException: [Z cannot be cast to [Ljava.lang.Object; at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:93) at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133) at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:90) at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133) at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:90) at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133) at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133) at com.jd.**.TdeProxy.$$FastClassBySpringCGLIB$$4fa3c52.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769) ..省略
02 问题分析
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将
问题描述
JaCoCo是一款被广泛应用于公司内部的开源覆盖率工具,将其引用至测试环境后,机器启动正常,但在操作下单时出现异常,阻塞下单流程。
去除JaCoCo配置、重新编译和部署后下单功能恢复正常。堆栈信息显示,问题源于系统对请求字段进行加密时出现异常,因为无法完成类型转换抛出异常,“[Z cannot be cast to [Ljava.lang.Object”,从而阻塞下单流程。
java.lang.ClassCastException: [Z cannot be cast to [Ljava.lang.Object;at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:93)at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133)at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:90)at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133)at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:90)at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133)at com.jd.**.TdeProxy.encryptObject(TdeProxy.java:133)at com.jd.**.TdeProxy.$$FastClassBySpringCGLIB$$4fa3c52.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)..省略
问题分析
1.报错代码
以下为报错处的代码片段,在将obj转换为Object[]时出现异常,既然已经识别出是数组,但是又无法完成类型转换,具体的原因需要进一步分析。
public void encryptObject(Object obj, String type) throws IllegalAccessException {/***省略***/if (Map.class.isAssignableFrom(clazz)) {/***省略***/} else if(Iterable.class.isAssignableFrom(clazz)) {/***省略***/} else if(clazz.isArray()) {/**********************报错代码行****************/for (Object o : (Object[]) obj) {/**********************报错代码行****************/this.encryptObject(o, type);}} else {Boolean encryptFlag = null;Field[] fields = this.getDeclaredFieldsAll(clazz);for (Field field : fields) {/***省略***/}/***省略***/for (Field field : fields) {Class fieldClazz = field.getType();if (fieldClazz == String.class) {/***省略***/} else {field.setAccessible(true);Object fieldValue = field.get(obj);this.encryptObject(fieldValue, type);}}}}
2.分析路径
public Field[] getDeclaredFieldsAll(Class clazz) {List<Field> fieldsList = new ArrayList<Field>();while (clazz != null) {Field[] declaredFields = clazz.getDeclaredFields();fieldsList.addAll(Arrays.asList(declaredFields));clazz = clazz.getSuperclass();}return fieldsList.toArray(new Field[fieldsList.size()]);}
由于已确认引入JaCoCo后对类进行了修改,只需触发任一流程以获取类的所有属性,通过设置断点并观察集合中的元素,即可查看具体修改情况。
3.追本溯源
(1)合成属性和方法
合成属性/方法是由Java编译器在编译过程中自动生成,并不是研发显示编写的,而是为了支持编译器内部的实现细节而生成的,下面针对合成方法进行一个举例说明。
public class Pack {public static void main(String[] args) {Pack.Goods goods = new Pack.Goods();System.out.println(goods.name);}private static class Goods {private String name = "手机";}}
import com.jd.ryan.test.Pack.1;class Pack$Goods {private String name;private Pack$Goods() {this.name = "手机";}Pack$Goods(1 x0) {this();}static String access$100(Pack$Goods x0) {return x0.name;}}
public class com.jd.ryan.test.Pack {public com.jd.ryan.test.Pack();Code:0: aload_01: invokespecial #1 //Method java/lang/Object."<init>":()Vpublic static void main(java.lang.String[]);Code:0: new #2 //class com/jd/ryan/test/Pack$Goods3: dup4: aconst_null5: invokespecial #3 //Method com/jd/ryan/test/Pack$Goods."<init>":(Lcom/jd/ryan/test/Pack$1;V8: astore_19: getstatic #4 //Field java/lang/System.out:Ljava/io/Printstream;12: aload_113: invokestatic #5 //Method com/jd/ryan/test/Pack$Goods.access$100:(Lcom/jd/ryan/test/Pack$Goods.access$100:(Lcom/jd/ryan/test/Pack$Goods;)Ljava/lang/String;16: invokevirtual #6//Method java/io/PrintStream.println:(Ljava/lang/String;)V19: return}
在代码实现中外部类直接打印内部类的name属性值,来看这行指令:
(2)JaCoCo原理简述
本文不再深入介绍JaCoCo的工作原理,感兴趣的同学可以查阅资料。
解决方法
List<Field> fieldsList = Arrays.stream(declaredFields).filter(field -> !field.isSynthetic()).collect(Collectors.toList());
总结
感谢阅读
▪功能支撑:会员成长体系、等级计算策略、权益体系、营销底层能力支持
▪用户活跃:会员关怀、用户触达、活跃活动、业务线交叉获客、拉新促活