Javassist 读写字节码

Javassist 是一个用于处理Java字节码的库。Java 字节码以二进制的形式存储在 class 文件中。每个 class 文件包含一个 Java 类或接口。

class 文件可以用 Javassist.CtClass 类来表示。CtClass对象用于处理 class 文件。以下是一个简单的例子,有两个类,这两个类没有关系,我们修改 Rectangle 使他的父类编程  Point。

ClassPool pool = ClassPool.getDefault();	
CtClass cc = pool.get("com.democome.Rectangle");	
cc.setSuperclass(pool.get("com.democome.Point"));	
cc.writeFile();

Rectangle.java

public class Rectangle {	
    private int width;	
    private int height;	
}

Point.java

public class Point {	
}

运行之后,用反编译工具可以看一下,继承关系已经改变:

640?wx_fmt=png

上面的代码首先获得一个 ClassPool。ClassPool 是 CtClass 的容器。它读取 class 文件来构造 CtClass 对象,并记录。要修改 calss,首先要从 ClassPool 通过 get( ) 获取一个 CtClass 对象。

关于实现的原理 ClassPool 中有一个 Hashtable 来存储 CtClass,key 就是类名。ClassPool 的 get( ) 方法如果能找到这个类则直接返回,否则创建一个 CtClass 对象返回,并存到 Hashtable 中。

privateHashtablecflow=null;

CtClass 对象对 class 文件进行修改,并调用 writeFile( ) 写入文件。Javassist 还提供了一种直接获取修改后的字节码的方法:

byte[] b = cc.toBytecode();

也可以直接加载 class:

Class clazz = cc.toClass();

定义一个类

定义一个类,需要调用 ClassPool 的 makeClass( ) 方法。

ClassPool pool = ClassPool.getDefault();	
CtClass cc = pool.makeClass("Point");

以上代码定义了一个类 Point 。可以用 CtNewMethod 创建一个方法,并使用 CtClass 中的 addMethod( ) 方法添加到 Point 。

cc.addMethod(CtNewMethod.make("public void hello(){System.out.print(\"hello\");}", cc));	
cc.writeFile();

640?wx_fmt=png

如果要创建新接口,可以用 ClassPool 中的 makeInterface( ) 方法。可以用 CtNewMethod 中的 abstractMethod( ) 创建接口中的成员方法。

ClassPool pool = ClassPool.getDefault();	
CtClass cc = pool.makeInterface("Point");	
cc.addMethod(CtNewMethod.abstractMethod(CtClass.voidType, "hello", null, null, cc));	
cc.writeFile();

640?wx_fmt=png

冻结类

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

冻结的 CtClass 可以解冻,以便允许修改类定义。例如,640?wx_fmt=png

解冻之后可以修改,如下:

ClassPool pool = ClassPool.getDefault();	
CtClass cc = pool.get("com.democome.Rectangle");	
cc.writeFile();	
cc.defrost();	
cc.setSuperclass(pool.get("com.democome.Point"));

如果 ClassPool.doPruning 设置为 true,那么当 Javassist 冻结该对象时,Javassist 会修剪 CtClass 对象中包含的数据结构。修剪过的 CtClass 对象无法再次解冻。ClassPool.doPruning 的默认值为 false 。要禁止修剪特定的 CtClass ,必须事先在该对象上调用 stopPruning( ):

类搜索路径

ClassPool.getDefault( ) 返回的 ClassPool,类搜索路径和 JVM 相同。如果程序在诸如 JBoss 和 Tomcat 之类的 Web 应用程序服务器上运行,那么 ClassPool 可能无法找到用户类,因为这样的 Web 应用程序服务器使用多个类加载器以及系统类加载器。在这种情况下,必须在 ClassPool 中注册其他类路径。

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

以上代码注册用于加载此引用的对象的类的类路径。可以使用任何 Class 对象作为参数而不是 this.getClass( )。用于加载由该 Class 表示的类的类路径。

还可以将目录注册为类搜索路径。例如,以下代码将目录 /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 仅用于搜索属于包 org.javassist 的类。例如,要加载类 org.javassist.test.Main ,其类文件将从以下位置获取:

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

还可以使用 ByteArrayClassPath 直接向 ClassPool 对象提供一个字节数组,并从该数组构造一个 CtClass 对象。例如:

ClassPool cp = ClassPool.getDefault();	
CtClass cc = cp.get("com.democome.Rectangle");	
byte[] b = cc.toBytecode();	
String name = "Rectangle";	
cp.insertClassPath(new ByteArrayClassPath(name, b));	
CtClass cc2 = cp.get(name);

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

ClassPool cp = ClassPool.getDefault();	
InputStream ins = an input stream for reading a class file;	
CtClass cc = cp.makeClass(ins);

例如:

ClassPool cp = ClassPool.getDefault();	
InputStream ins = new FileInputStream(	
        "/Users/yangpeng/Documents/workspace/javassist/Javassist/com/democome/Rectangle.class");	
CtClass cc = cp.makeClass(ins);	
System.out.println(cc.getName());

打印结果如下:

com.democome.Rectangle

结语

为了更好的交流互动,我们奇舞移动创建了一个技术交流微信群,我们可以聊一些流行技术,也可以聊聊工作中遇到的问题,对我们公众号有什么意见和建议也欢迎提出,扫码进群

640?wx_fmt=jpeg

也可以加小编微信,备注奇舞移动噢

640?wx_fmt=jpeg

--END--

识别二维码,关注我们

640?wx_fmt=png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值