`
564615061
  • 浏览: 12572 次
  • 性别: Icon_minigender_1
  • 来自: 石家庄
社区版块
存档分类
最新评论

javassist教程

阅读更多
Javassist是一个执行字节码操作的强而有力的驱动代码库。它允许开发者自由的在一个已经编译好的类中添加新的方法,或者是修改已有的方法。但是, 和其他的类似库不同的是,Javassist并不要求开发者对字节码方面具有多么深入的了解,同样的,它也允许开发者忽略被修改的类本身的细节和结构。
字 节码驱动通常被用来执行对于已经编译好的类的修改,或者由程序自动创建执行类等等等等相关方面的操作。这就要求字节码引擎具备无论是在运行时或是编译时都 能修改程序的能力。当下有些技术便是使用字节码来强化已经存在的Java类的,也有的则是使用它来使用或者产生一些由系统在运行时动态创建的类。举例而 言,JDO1.0规范就使用了字节码技术对数据库中的表进行处理和预编译,并进而包装成Java类。特别是在面向对象驱动的系统开发中,相当多的框架体系 使用字节码以使我们更好的获得程序的范型性和动态性。而某些EJB容器,比如JBOSS项目,则通过在运行中动态的创建和加载EJB,从而戏剧性的缩短了 部署EJB的周期。这项技术是如此的引人入胜,以至于在JDK中也有了标准的java.lang.reflect.Proxy类来执行相关的操作。

但是,尽管如此,编写字节码对于框架程序开发者们而言,却是一个相当不受欢迎的繁重任务。学习和使用字节码在某种程度上就如同使用汇编语言。这使得于大多数 开发者而言,尽管在程序上可以获得相当多的好处,可攀登它所需要的难度则足以冷却这份热情。不仅如此,在程序中使用字节码操作也大大的降低了程序的可读性 和可维护性。

这是一块很好的奶油面包,但是我们却只能隔着橱窗流口水 难道我们只能如此了吗?

所幸的是,我们还有Javassist。Javassist是一个可以执行字节码操作的函数库,可是尽管如此,它却是简单而便与理解的。他允许开发者对自己的程序自由的执行字节码层的操作,当然了,你并不需要对字节码有多深的了解,或者,你根本就不需要了解。

API Parallel to the Reflection API

Javassist 的最外层的API和JAVA的反射包中的API颇为类似。它使你可以在装入ClassLoder之前,方便的查看类的结构。它主要由 CtClass,,CtMethod,,以及CtField几个类组成。用以执行和JDK反射API中 java.lang.Class,,java.lang.reflect.Method,, java.lang.reflect.Method .Field相同的操作。这些类可以使你在目标类被加载前,轻松的获得它的结构,函数,以及属性。此外,不仅仅是在功能上,甚至在结构上,这些类的执行函 数也和反射的API大体相同。比如getName,getSuperclass,getMethods,,getSignature,等等。如果你对 JAVA的反射机制有所了解的话,使用Javassist的这一层将会是轻松而快乐的。


接下来我们将给出一个使用Javassist来读取org.geometry.Point.class的相关信息的例子(当然了,千万不要忘记引入javassist.*包):


1. ClassPool pool = ClassPool.getDefault();


2. CtClass pt = pool.get("org.geometry.Point");


3. System.out.println(pt.getSuperclass().getName());


其中,ClassPool是CtClas 的创建工厂。它在classpath中查找CtClass的位置,并为每一个分析请求创建一个CtClass实例。而“getSuperclass().getName()”则展示 出org.geometry.Point.class所继承的父类的名字。

但是,和反射的API不尽相同的 是,Javassist并不提供构造的能力,换句话说,我们并不能就此得到一个org.geometry.Point.class类的实例。另一方面,在该类没有实例化前,Javassist也不提供对目标类的函数的调用接口和获取属性的值的方法。在分析阶段,它仅仅提供对目标类的类定义修改,而这点,却是反射API所无法做到的。


举例如下:


4. pt.setSuperclass(pool.get("Figure"));


这样做将修改目标类和其父类之间的关系。我们将使org.geometry.Point.clas改继承自Figure类。当然了,就一致性而言,必须确保Figure类和原始的父类之间的兼容性。


而往目标类中新增一个新的方法则更加的简单了。首先我们来看字节码是如何形成的:


5. CtMethod m = CtNewMethod.make("public int xmove(int dx) { x += dx; }", pt);


6. pt.addMethod(m);


CtMethod类的让我们要新增一个方法只需要写一段小小的函数。这可是一个天大的好消息,开发者们再也不用为了实现这么一个小小的操作而写一大段的虚拟机指令序列了。Javassist将使用一个它自带的编译器来帮我们完成这一切。


最后,千万别忘了指示Javassist把已经写好的字节码存入到你的目标类里:


7. pt.writeFile();


writeFile方法可以帮我们把修改好了的定义写到目标类的.class文件里。当然了,我们甚至可以在该目标类加载的时候完成这一切,Javassist可以很好的和ClassLoader协同工作,我们不久就将看到这一点。


Javassist 并不是第一套用以完成从代码到字节码的翻译的函数库。Jakarta的BCEL也是一个比较知名的字节码引擎工具。但是,你却无法使用BCEL来完成代码 级别的字符码操作。如果你需要在一个已经编译好的类中添加一个新的方法,假如你用的是BCEL的话,你只能定义一段由那么一大串字符码所构成的指令序列。 正如上文所说,这并不是我们所希望看到的。因此,就此方面而言,Javassis使用代码的形式来插入新的方法实在是一大福音。


Instrumenting a Method Body


和 方法的新增一样,对于一个类的方法的其他操作也是定义在代码层上的。换而言之,尽管这些步骤是必须的,开发者们也同样无须直接对虚拟机的指令序列进行操作 和修改,Javassis将自动的完成这些操作。当然了,如果开发者认为自己有必要对这些步骤进行管理和监控,或者希望由自己来管理这些操作的 话,Javassist同样提供了更加底层的API来实现,不过我们在这篇文章中将不会就此话题再做深入探讨。恩,尽管从结构而言,它和BCEL的字节码 层API差不多。


设计Javassist对目标类的子函数体的操作API的设想立足与ASPect-Oriented Programming(AOP)思想。Javassist允许把具有耦合关系的语句作为一个整体,它允许在一个插入语句中调用或获取其他函数或者及属性 值。它将自动的对这些语句进行优先级分解并执行嵌套操作。


如下例所示,清单1首先包含了一个CtMethod,它主要针对 Screen类的draw方法。然后,我们定义一个Point类,该类有一个move操作,用来实现该Point的移动。当然了,在移动前,我们希望可以 通过draw方法得到该point目前的位置,那么,我们需要对该move方法加增如下的定义:


{ System.out.println("move"); $_ = $proceed($$); }


这样,在执行move之前,我们就可以打印出它的位置了。请注意这里的调用语句,它是如下格式的:


$_ = $proceed($$);


这样我们就将使用原CtMethod类中的process()对该point的位置进行追踪了。


基 与如上情况,CtMethod的关于methord的操作其实被划分成了如下步骤,首先,CtMethod的methord将扫描插入语句(代码)本身。 一旦发现了子函数,则创建一个ExprEditor实例来分析并执行这个子函数的操作。这个操作将在整个插入语句执行之前完成。而假如这个实例存在某个 static的属性,那么methord将率先检测对插入语句进行检测。然后,在执行插入到目标类---如上例的point类---之前,该static 属性将自动的替换插入语句(代码)中所有的相关的部分。不过,值得注意的是,以上的替换操作,将在Javassist把插入语句(代码)转变为字节码之后 完成。


Special Variables


在替换的语句(代码)中,我们也有可能需要用到一些特殊变量 来完成对某个子函数的调用,而这个时候我们就需要使用关键字“$”了。在Javassist中,“$”用来申明此后的某个词为特殊参数,而“$_”则用来 申明此后的某个词为函数的回传值。每一个特殊参数在被调用时应该是这个样子的“$1,$2,$3 ”但是,特别的,目标类本身在被调用时,则被表示为 “$0”。这种使用格式让开发者在填写使用子函数的参数时轻松了许多。比如如下的例子:


{ System.out.println("move"); $_ = $proceed($1, 0); }


请注意,该子函数的第2个参数为0。


另 外一个特殊类型则是$arg,它实际上是一个容纳了函数所有调用参数的Object队列。当Javassist在扫描该$arg时,如果发现某一个参数为 JAVA的基本类型,则它将自动的对该参数进行包装,并放入队列。比如,当它发现某一个参数为int类型时,它将使用java.lang.integer 类来包装这个int参数,并存入参数队列。和Java的反射包:java.lang.reflect.Methord类中的invoke方法相 比,$args明显要省事的多。


Javassist也同样允许开发者在某个函数的头,或者某个函数的尾上插入某段语句(代码)。比如,它有一个insertBefore方法用以在某函数的调用前执行某个操作,它的使用大致是这个样子的:


1. ClassPool pool = ClassPool.getDefault();
2. CtClass cc = pool.get("Screen");
3. CtMethod cm = cc.getDeclaredMethod("draw", new CtClass[0]);
4. cm.insertBefore("{ System.out.println($1); System.out.println($2); }");
5. cc.writeFile();


以上例子允许我们在draw函数调用之前执行打印操作---把传递给draw的两个参数打印出来。


同样的,我们也可以使用关键字$对某一个函数进行修改或者是包装,下面就


1. CtClass cc = sloader.get("Point");
2. CtMethod m1 = cc.getDeclaredMethod("move");
3. CtMethod m2 = CtNewMethod.copy(m1, cc, null);
4. m1.setName(m1.getName() + "_orig");
5. m2.setBody("{ System.out.println("call"); return $proceed($$);
}", "this", m1.getName());
6. cc.addMethod(m2);
7. cc.writeFile();


以上代码的前四行不难理解,Javassist首先对Point中的move方法做了个拷贝,并创建了一个新的函数。然后,它把存在与Point类中的原 move方法更名为“_orig”。接下来,让我们关注一下程序第五行中的几个参数:第一个参数指示该函数的在执行的最初部分需要先打印一段信息,然后执 行子函数proceed()并返回结果,这个和move方法差不多,很好理解。第二个参数则只是申明该子函数所在的类的位置。这里为this即为 Point类本身。第三个参数,也就是“m1.getName()”则定义了这个新函数的名字。


Javassist也同样具有其他的操作和类来帮助你实现诸如修改某一个属性的值,改变函数的回值,并在某个函数的执行后补上其他操作的功能。您可以浏览www.javassist.org以获得相关的信息。
分享到:
评论

相关推荐

    java 反编译工具 jboss-javassist

    对于初学者,可以通过网上的教程、文档或者示例代码来快速上手。遇到问题时,可以查阅Javassist的官方文档,或者在相关的论坛和社区寻求帮助。Javassist是一个强大而灵活的工具,熟练掌握它将极大地提升开发效率和...

    javassistDemo.zip

    总结来说,"javassistDemo.zip"是一个实践教程,通过实例演示了如何使用Javaassist库来动态修改Java类,包括插入新的方法和改变方法的实现。这对于理解和掌握Java运行时代码修改技术,尤其是对于那些需要在运行时...

    javassist-3.15.0-GA

    - **文档**:官方提供详细的API文档和教程,帮助开发者快速上手。 - **示例代码**:通过阅读和实践示例代码,可以更好地理解Javaassist的工作原理和使用方式。 - **社区支持**:Javaassist有一个活跃的社区,遇到...

    javassist-3.18.0-GA

    这个文件可能是一个示例或教程,解释了如何利用 Javaassist 修改已存在的 `.class` 文件。通过读取这个文件,开发者可以学习到如何在运行时改变类的行为,例如添加新的方法、修改现有方法的字节码或者插入切面逻辑...

    javassist18,20,22三个版本.zip

    初学者可以从官方文档、教程和示例代码开始,逐步熟悉其API和使用方式。理解如何创建、修改和加载类是关键,同时注意版本间的兼容性和性能差异。 总的来说,Javaassist是Java开发中一个强大的工具,尤其在需要动态...

    javassist-3.11

    8. **学习资源**:为了更好地理解和使用Javassist,开发者可以参考其官方文档,以及各种在线教程和示例代码。社区支持也相当活跃,许多开发者分享了他们的经验和技巧。 总的来说,Javassist是一个功能强大且灵活的...

    JavaAgent:Javassist 与 Asm JavaAgent 字节码动态编程项目

    JavaAgent是一种强大的技术,它允许在...项目中可能包含示例代码、教程文档,以及一系列的练习,帮助你掌握这些技术。完成这个项目后,你将在字节码编程和JavaAgent应用方面具备坚实的基础,能够解决更高级的编程挑战。

    javassist3150gajar_jb51

    其他 URL 文件,如 "去脚本之家看看.url"、"领取天猫淘宝内部优惠券.url" 和 "服务器软件.url" 看起来与 Javaassist 或 JAR 文件本身的技术内容关系不大,可能是为了提供额外的信息资源,如教程链接、优惠信息或其他...

    javassist-example

    Javaassist 是一个强大的 Java 字节码操作库,它允许开发者在运行时动态修改或创建类。这个名为 "javassist-example" 的项目很可能是用来演示...记得结合官方文档和在线教程,以便更深入地理解其工作原理和最佳实践。

    SpringMVC教程中所需要的jar

    11. **javassist.jar**:在某些Spring AOP实现中,如CGLIB代理,需要这个库来进行动态类生成。 为了成功运行Spring MVC教程中的实例,确保所有这些jar文件都被正确地导入并包含在类路径中至关重要。这不仅使我们...

    MyEclipse开发SSH2(Struts2+Spring+Hibernate)教程

    - javassist-3.7.ga.jar - ognl-3.0.jar - struts2-core-2.2.1.jar - xwork-core-2.2.1.jar 这些包应提前解压到某一目录(如C:\struts),便于统一管理。 #### 三、配置Struts过滤器 为了使Web工程能够识别并...

    testAgent.zip

    在本教程中,我们将深入探讨如何使用javassist进行插桩,以便检测项目中任何方法的耗时时间,实现无侵入式的监控。 首先,让我们了解什么是插桩(Profiling)。插桩是动态程序分析的一种技术,通过在源代码或字节码...

    eclipse搭建教程

    - 另外还需要导入以下三个JAR包来避免运行时可能出现的异常:`commons-io-1.3.2.jar`, `commons-fileupload-1.2.1.jar`, `javassist-3.7.ga.jar`(此包位于`struts2-blank-2.2.1.war`示例工程的`web-inf/lib`目录下...

    Hibernate3.3.2_Java例子精简JAR包

    本例子通過學習“002_尚学堂马士兵_Java视频教程_Hibernate3.3.2_HelloWorld.avi”教程實踐的小例子。 整理出来的精简JAR包,共八个。分别: antlr-2.7.6.jar、commons-collections-3.1.jar、dom4j-1.6.1.jar、...

    robotium测试很好的教程

    这种能力是利用Java字节码的深入理解或借助开源字节码项目如bcel、ASM或javassist等来实现的。例如,Btrace工具就是基于Instrumentation和ASM的,理解了这两者的原理,就更容易掌握Btrace。 Robotium还提供了数据...

    《EJB3.0实例子教程》jar包1

    《EJB3.0实例子教程》jar包1包含了多个重要的Java企业级开发库,这些库主要用于实现EJB(Enterprise JavaBeans)3.0规范,该规范是Java平台企业版(Java EE)的一部分,用于构建分布式、事务处理、安全性和可伸缩的...

    struts2.3.8配置教程

    - javassist-3.11.0.GA.jar - ... * src + com - action - IndexAction.java 其中,WEB-INF文件夹中包含了web.xml文件和lib文件夹,lib文件夹中包含了Struts2.3.8所需的所有jar包。 2. 修改web.xml 在...

    Java实例化一个抽象类对象的方法教程

    在Android开发中,由于Javassist库在Android环境中可能不兼容,所以开发者可能需要寻找替代方案,比如使用注解处理器(Annotation Processor)。通过注解处理器,可以在编译期间生成源代码,从而实现对抽象类的扩展...

Global site tag (gtag.js) - Google Analytics