Hi All,
问题的提出 :
Unit Test 的运行需要基于MockEJB ,而产品不支持MockEJB ,客户也不同意添加支持,所以尝试通过不修改源文件的情况下,让产品支持MockEJB 。
研究发现,让产品支持MockEJB ,就需要修改ProxyFactory 类getImplementationClass 方法。
解决之路:
Ø 简述:
一开始研究Cglib 和ASM ,发现在内存中直接修改ProxyFactory 类,实现起来很困难,原因有二:
1. 要修改的方法是一个静态的,无法用子类来修改它的行为。
2. 产品是间接调用ProxyFactory (没办法在调用的入口做手脚)
Cglib 和ASM 的一些使用例子,都是使用生成的子类或加载新构造的类来进行操作,在入口做了改变,而这对于我们行不通。
Aspectj 可以做到我们要的,但是性能太差,所以不予考虑(Gene 的建议)。
于是,我们尝试使用ASM 修改ProxyFactory 类,然后写成class 文件替代原class 文件(物理文件)。修改成功。
Ø 细节:
1) 研究 Cglib ,发现资料少,不能解决问题
2) 转研究ASM ,发现了解决问题的可能,但技术太底层,需要了解字节码
3) 使用ASM ,成功修改ProxyFactory 的getImplementationClass 方法
4) 发现需要为ProxyFactory 添加一个无参的构造方法
无参构函的添加对于java 代码的添加很容易,对于字节码的添加却很难,因为需要初始化很多变量。
5) 研究发现:有工具(ASMifierClassVisitor )可以把普通类文件转换为对应的ASM 代码(使用ASM 的JAVA 代码),于是为了创建生成JAVA 类的ASM 代码,我们可以先写普通的JAVA 代码,然后使用这个工具来转换。
6) 基于工程的修改成功
7) 研究基于Build 安装后目录的(基于jar 包)动态修改
8) 发现奇怪的问题(Jar 包冲突) ,Ant Build 无法读取类(设置fork=true )等问题,解决它们
9) 修改后的Jar 在运行时报错,修改后的Jar 有问题
10) 研究发现第一次修改类并运行不成功,第二次运行可以成功
11) 在Ant 的Build.xml 中,使用一个target 来运行修改类文件,另一个来运行JUnit ,运行成功 。
总结:
从这个问题的提出,到目前的解决,经历了一个漫长的探索,尝试过程,这个过程中对于以后的工作有几点借鉴:
ü 我们有成功的案例可以轻松的修改Class 文件,修改所有变量的值或方法的行为,这么做在有些特殊情况下能起到很好的效果
ü 问题的解决可以更加的灵活
比如对于修改Jar 文件中的特定Class 文件中,出现第一次修改运行失败,第二次运行成功,那么很明显在第一次的修改后运行的仍然是旧的Class 文件,于是很容易想到让JVM 重载修改后的类,可是这么做很麻烦,经过Bruce 的建议,我们尝试使用两个target 来运行我们的程序,一个专门用来修改类文件,另一个来运行Junit Test, 并设置运行时的fork=true, 那么第二次的运行将基于新的JVM ,那么就会使用最新的类来运行,于是问题得到了解决。
ü 可以请教身边的一些同事
对于这个问题我先后请教过 Gene , Shawn , June , Bruce
在此,也谢谢 June 和 Bruce 的帮助!
Regards,
Rex Wu