问题来源
下载现场部署的代码的源代码,虽然只有 .class
文件,但是也能够通过反编译获得一些信息,果不其然,其中第一次查询的固定的某个 id 与现在项目中对应的 id 值不一致,然后在部署现场执行 sql 语句发现没有数据,定位到原因在此。
解决办法
现在我这里的源代码已经被改得面目全非,要改回去是不可能的了。由于是在项目中写的一个固定数字,那么通过分析现场获取过来的项目的 .class
文件,就能够定位到,然后通过二进制查看器,对其进行二进制级别的修改,就能够达到修复这个 BUG 的目的。
解决过程:
1、获取 AServiceImp.class
文件,通过反编译查看器得到需要修改的代码如下:
public PageDataModel queryCount(String startTime, String endTime) {
List resultList = new ArrayList();
List aList = this.ADao.queryA(1, 356);
....
Double timeTotal = Double.valueOf(0.0D);
Double sizeTotal = Double.valueOf(0.0D);
Integer countTotal = Integer.valueOf(0);
List footerList = new ArrayList();
....
return new PageDataModel(Integer.valueOf(0), resultList, footerList);
}
其中需要修改的是:List aList = this.ADao.queryA(1, 356);
将 356 修改为 610。
2、通过 javap -verbose AServiceImp
命令,输出 AServiceImpl.class
中的字节码内容,并查找上述程序段的字节码全部内容如下:
public com.example.web.PageDataModel queryCount(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)Lcom/example/web/PageDataModel;
flags: ACC_PUBLIC
Code:
stack=9, locals=13, args_size=3
0: aload_1
1: ifnull 26
4: aload_2
5: ifnull 26
8: aload_1
9: ldc #4 // String
11: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
14: ifne 26
17: aload_2
18: ldc #4 // String
20: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
23: ifeq 33
26: aconst_null
27: astore_1
28: aconst_null
29: astore_2
30: goto 73
33: new #8 // class java/lang/StringBuilder
36: dup
37: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
40: aload_1
41: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: ldc #11 // String 00:00:00
46: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
52: astore_1
53: new #8 // class java/lang/StringBuilder
56: dup
57: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
60: aload_2
61: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
64: ldc #13 // String 23:59:59
66: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
69: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
72: astore_2
73: new #17 // class java/util/ArrayList
76: dup
77: invokespecial #18 // Method java/util/ArrayList."<init>":()V
80: astore_3
81: aload_0
82: getfield #24 // Field ADao:Lcom/example/web/ADao;
85: iconst_1
86: sipush 356
89: invokevirtual #155 // Method com/example/web/dao/ADao.queryA:(II)Ljava/util/List;
92: astore 4
94: aload 4
96: invokeinterface #29, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
101: astore 5
103: aload 5
105: invokeinterface #30, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
110: ifeq 397
113: aload 5
115: invokeinterface #31, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
120: checkcast #87 // class com/example/web/AInfo
123: astore 6
125: aload_0
126: getfield #24 // Field statisticsDao:Lcom/example/web/dao/ADao;
129: iconst_2
130: aload 6
....
其中 356 所在的位置为:
85: iconst_1
86: sipush 356
89: invokevirtual #155 // Method
3、使用二进制查看器打开 AServiceImpl.class
文件。
4、查阅第 2 步中 356 前后的助记符所对应的十六进制字节码:
iconst_1 --- 0x04
sipush --- 0x11
invokevirtual --- 0xb6
5、由于 iconst_1
与 sipush
之间没有任何间隔,那么就可以通过 0x04
加 0x11
在二进制查看器中同时进行搜索,然后比对后面几个二级制数字中是否出现 0xb6
,查找结果如下:
04 11 01 64 b6
04 11 13 88 b6
计算在 04 11
和 b6
之间的十六进制数字得到:
0x0164 = 356
0x1388 = 5000
可以得出:04 11 01 64 b6
就是需要修改的 356 这个数字,然后把 610 转换为十六进制为 0x0262
,最后在二进制查看器中将 0x0164
修改为 0x0262
即可。
6、验证,保存上一步修改的文件,此时文件依然为 AServiceImpl.class
文件,然后通过 javap -verbose AServiceImpl
,找到 sipush
对应的那一行,可以看到此时后面的数据已经变成了 610,修改成功。
85: iconst_1
86: sipush 610
89: invokevirtual
替换现场中的 .class
文件,能看到数据了,修改成功。
总结
本文只是给出了修改固定值的一种便捷方法,不适用于普遍的情况,但是在本文给出的前提条件下,不失为一种巧妙的解决方式。