Javassist

1、简介

Javassist(JAVA programming ASSISTant)是在Java中编辑字节码的类库;它使Java程序能够在运行时定义一个新类,并在JVM加载是修改类文件。

我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,他就是Javassist。

与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:源级别和字节码级别。如果用户使用源级API,他们可以编辑类文件,而不知道Java字节码的规格。整个API只用Java语言的词汇来设计。您甚至可以以源文本的形式指定插入的字节码;Javassist在运行中编译它。另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。

在Javassist中,进行类表述的基本单元是CtClass(即“编译时的类”,compile time class)。组成程序的这些类会存储在一个ClassPool中,它本质上就是CtClass实例的一个容器。

ClassPool的实现使用了一个HashMap,其中key是类的名称,而value是对应的CtClass对象。

正常的Java类都会包含域、构造器以及方法。在CtClass中,分别与之对应的是CtField、CtConstructor和CtMethod。要定位某个CtClass,我们可以根据名称从ClassPool中获取,然后通过CtClass得到任意的方法,并做出我们的修改。如下所示:

Javassist提供的javassist.util.HotSwapper(3.1之前则是javassist.tools.HotSwapper,BTrace也是使用HotSwapper机制)类能够更加方便的动态重新加载类

虽然Javassist能够提供动态重新加载类的功能,不过由于它要求启用JPDA



作者:阿术和薇薇安
链接:https://www.jianshu.com/p/27337d7ca4c7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2、读取和写入字节码

类Javassist.CtClass是类文件的抽象表示形式。CtClass(编译时类)对象是处理类文件的句柄。下面的程序是一个非常简单的示例:

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("test.Rectangle");

cc.setSuperclass(pool.get("test.Point"));

cc.writeFile();

该程序首先获得一个ClassPool对象,它通过Javassist对象字节码修改。ClassPool对象是表示类文件的CtClass对象的容器。它根据需要读取类文件以构造CtClass对象,并记录构造对象以响应以后的访问。若要修改类的定义,用户必须首先从ClassPool对象获取对表示该类的CtClass对象的引用。ClassPool对象获得的,它被分配给一个变量cc。getDefault返回的ClassPool对象搜索默认的系统搜索路径。

可以修改从ClassPool对象获得CtClass对象(稍后将介绍如何修改CtClass的详细信息)。在上面的例子中,它被修改以便测试的超类。将矩形更改为类测试点。当最终调用CtClass()中的writeFile()时,此更改将反映在原始类文件中。

writeFile()时,此更改将反映在原始类文件中。

writeFile()将CtClass对象转换为类文件,并将其写入本地磁盘。Javassist还提供了一种直接获取修改后的字节码的方法。要获取字节码,请调用toBytecode():

byte[] b = cc.toBytecode();

您还可以直接加载CtClass:

Class clazz = cc.toClass();

toClass()请求当前线程的上下文类加载程序加载由CtClass表示的类文件。它返回一个表示已加载类的java.lang.Class对象。

2.1、定义类

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.makeCLass("Point");

此程序定义一个类Point,包括没有成员。可以使用CtNewMethod中声明的工厂方法创建点的成员方法,并在CtClass中追加到点与addMethod()。

makeClass()无法创新接口;可以使用 makeInterface () 做。接口中的成员方法可以在 CtNewMethod 中使用 abstractMethod () 创建。请注意, 接口方法是一种抽象方法。

2.2、冻结类

如果CtClass对象由writeFile()、toClass()或toBytecode()转换为类文件,Javassist将冻结该CtClass对象。那CtClass对象的进一步修改不被允许。这是为了在开发人员试图修改已加载的类文件时发出警告,因为JVM不允许重新加载类。

冻结的CtClass可以解冻,一遍允许对类定义进行修改。例如,

CtClass cc = ...;


cc.writeFile();

cc.defrost();

cc.setSuperclass(...); // OK since the class is not frozen.

2.3、类搜索路径

静态方法ClassPool.getDefault()返回的默认ClassPool将搜索底层JVM(Java虚拟机)具有的同一路径。如果某个程序在web应用程序服务器(如JBoss和Tomcat)上运行,则ClassPool对象可能无法找到用户类,因为这样的web应用程序服务器使用多个类加载器以及系统类加载程序。在这种情况下,必须将附加的类路径注册到ClassPool。假设池引用的是ClassPool对象:

pool.insertClassPath(new ClassClassPath(this.getClass()));

此语句注册用于加载次饮用的对象的类的类路径。可以将任何类对象用作参数而不是this.getClass()。用于加载由该类对象表示的类的类路径已注册。可以将目录名注册为类搜索路径。例如,下面的代码将目录/usr/local/javalib添加到搜索路径中:

ClassPool pool = ClassPool.getDefault();

pool.insertClassPath("/usr/local/javalib");

用户可以添加的搜索路径不仅是一个目录,而且可以是一个URL:

ClassPool pool = ClassPool.getDefault();

ClassPath cp - new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");

pool.insertClassPath(cp);

此程序将"http://www.javassist.org:80/java/"添加到类搜索路径中。此URL仅用于搜索属于包组织javassist的类。例如,要加载类org.javassist.test.Main,将从以下内容获取其类文件:

http://www.javassist.org:80/java/org/javassist/test/Main.class

此外,您可以直接给ClassPool对象一个字节数组,并从该数组构造一个CtClass对象。为此,请使用ByteArrayClassPath.例如:

ClassPool cp = ClassPool.getDefault();

byte[] b = a byte array;

String name = class name;

cp.insertClassPath(new ByteArrayClassPath(name, b));

CtClass cc = cp.get(name);

获得的CtClass对象表示由b指定的类文件定义的类。ClassPool从给定的ByteArrayClassPath读取类文件(如果调用了get(),并且给定的类名为get()等于名称指定的类别。

如果您不知道该类的完全限定名,则可以在ClassPool中使用makeClass():

ClassPool cp = ClassPool.getDefault();

InputStream ins = an input stream for reading a class file;

CtClass cc = cp.makeClass(ins);

 makeClass()从给定输入流返回构造的CtClass对象。您可以使用makeClass()将类文件送到ClassPool对象。如果搜索路径包含大jar文件,这可能会提高性能。由于ClassPool对象根据需要读取类文件,因此它可能会反复搜索每个类文件的整个jar文件。makeClass()可用于优化此搜索。由makeClass()构造的CtClass保存在ClassPool对象中,不再读取类文件。

3、小结

本文简要介绍了javaassist及其简单用法。会有一些读者好奇:它和AOP有什么关系和区别?举个简单的例子即可:CGLib是动态代理的经典类库,其底层实现使用ASM,javaassist是类似ASM的东东。

4、参考文献

  1. javassist
  2. Java动态编程初探——Javassist
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值