原文地址:奇舞移动技术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 {
}
复制代码
运行以后,用反编译工具能够看一下,继承关系已经改变:数据结构
上面的代码首先得到一个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();
复制代码
若是要建立新接口,能够用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();
复制代码
若是经过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
复制代码