快速业务通道

Java编程的动态性,第6部分: 利用Javassist进行面向方面的更改 - 编程入门网

作者 佚名技术 来源 NET编程 浏览 发布时间 2012-06-17
直接运行清单 2 中的 中的 BeanTest 程序,则输出如下:

[dennis]$ java -cp . BeanTest Bean values are originalA and originalB Bean values are newA and newB

如果用 清单 1 中的 TranslateConvert 程序运行它并指定监视其中的一个 set 方法, 那么输出将如下所示:

[dennis]$ java -cp .:javassist.jar TranslateConvert Bean setA  BeanTest Bean values are originalA and originalB Call to set value newA Bean values are newA and newB

每项工作都与以前一样,但是现在在执行这个程序时,所选的方法被调用时会有一个通知 。

在这个例子中,可以用其他的方法容易地实现同样的效果,例如通过使用 第 4 部分 中 的技术在实际的 set 方法体中增加代码。这里的区别是,在使用位置增加代码让我有了灵活 性。例如,可以容易地修改 TranslateConvert.ConverterTranslatoronWrite() 方法来检查 正在加载的类名,并只转换在我想要监视的类的清单中列出的类。直接在 set 方法体中添加 代码无法进行这种有选择的监视。

系统字节码转换由于提供了灵活性而使其成为为标准 Java 代码实现面向方面的扩展的强 大工具。在本文后面您会看到更多这方面的内容。

Java编程的动态性,第6部分: 利用Javassist进行面向方面的更改(3)

时间:2011-04-09 IBM Dennis M. Sosnoski

转换限制

由 CodeConverter 处理的转换很有用,但是有局限性。例如,如果希望在调用目标方法 之前或者之后调用一个监视方法,那么这个监视方法必须定义为 static void 并且必须先接 受一个目标方法的类的参数,然后是与目标方法所要求的同样数量和类型的参数。

这种严格的结构意味着监视方法需要与目标类和方法完全匹配。举一个例子,假设我改变 了 清单 1 中 reportSet() 方法的定义,让它接受一个一般性的 java.lang.Object 参数, 想使它可以用于不同的目标类:

public static void reportSet(Object target, String value) {      System.out.println("Call to set value " + value);    }

编译没有问题,但是当我运行它时它就会中断:

[dennis]$ java -cp .:javassist.jar TranslateConvert Bean setA  BeanTest Bean values are A and B java.lang.NoSuchMethodError: TranslateConvert.reportSet (LBean;Ljava/lang/String;)V      at BeanTest.changeValues(BeanTest.java:17)      at BeanTest.main(BeanTest.java:23)      at ...

有办法绕过这种限制。一种解决方案是在运行时实际生成与目标方法相匹配的自定义监视 方法。不过这要做很多工作,在本文中我不打算试验这种方法。幸运的是,Javassist 还提 供了另一种处理系统字节码转换的方法。这种方法使用 javassist.ExprEditor ,与 CodeConverter 相比,它更灵活、也更强大。

容易的类剖析

用 CodeConverter 进行字节码转换与用 javassist.ExprEditor 的原理一样。不过, ExprEditor 方式也许更难理解一些,所以我首先展示基本原理,然后再加入实际的转换。

清单 3 显示了如何用 ExprEditor 来报告面向方面的转换的可能目标的基本项目。这里 我在自己的 VerboseEditor 中派生了 ExprEditor 子类,重写了三个基本的类方法 ―― 它 们的名字都是 edit() ,但是有不同的参数类型。如 清单 1 中的代码,我实际上是在 DissectionTranslator 内部类的 onWrite() 方法中使用这个子类,对从 ClassPool 实例中 加载的每一个类,在对类对象的 instrument() 方法的调用中传递一个实例。

清单 3. 一个类剖析程序

public class Dissect {    public static void main(String[] args) {     

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号