Javassist 读写字节码

原文地址:奇舞移动技术java

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 {

}
复制代码

运行以后,用反编译工具能够看一下,继承关系已经改变:数据结构

1

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

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

private Hashtable cflow = null;spa

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

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();
复制代码

2

若是要建立新接口,能够用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();
复制代码

3

冻结类

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

冻结的CtClass能够解冻,以便容许修改类定义。 例如,

解冻以后能够修改,以下:

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);
复制代码

以上代码将“www.javassist.org:80/java/”添加到类搜…

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
复制代码

关注微信公众号,最新技术干货实时推送

image
相关文章
相关标签/搜索