Javasssist初探(需要对Java的ClassLoader机制有一些了解)
Javassist可以用来动态生成java类,就好像js可以生成可执行的js代码一样。javassist是JBoss的一个子项目,用处的话可能是在对EJB提供支持的时候,将声称各种接口的实现类和代理类。
下面给出一个例子,在这个例子中一共有三个类:DirectLoader自定义类加载器;IAccess是接口类(我们动态生成的类要实现这个接口) ;MainTest完成主要逻辑的类,其中javassist的使用也在这里面体现。下面给出三个类的源代码。需要使用javassist的jar包程序才能运行
//--------------DirectLoader------------------------------------
import java.security.SecureClassLoader;
public class DirectLoader extends SecureClassLoader{
protected DirectLoader() {
super(MainTest.class.getClassLoader());
}
protected Class load(String name, byte[] data) {
return super.defineClass(name, data, 0, data.length);
}
}
//--------------IAccess------------------------------------
import java.util.List;
public interface IAccess{
public void Calculate(List InVars) throws Exception;
}
//--------------MainTest------------------------------------
import java.util.List;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
/*
* 本类用来演示如何使用javassist动态生成
*/
public class MainTest {
private static final CtClass[] NO_ARGS = {};
//private static final CtClass[] INT_ARGS = { CtClass.intType };
private static boolean isFirst = true;
// 用这个标示标记是否需要重新生成新的类。
// 一般情况下,我们没必要动态生成类,如果有这个必要
// 就说明类需要动态改动并使用,所以,这个变量是很有用的
private static boolean needReload = true;
private static DirectLoader s_classLoader = new DirectLoader();
public MainTest() {
}
public static void main(String[] args) throws Exception {
execTest();
execTest();
}
/**
* 执行代码
*
* @throws Exception
*/
public static void execTest() throws Exception {
Class clas = null;
try {
if (needReload) {
clas = createExecClass();
} else {
clas = s_classLoader.loadClass("ExecVar");
}
} catch (ClassNotFoundException e) {
System.out.println("没有这个类,动态生成");
clas = createExecClass();
}
// 使用动态生成的类,最好是使用接口。否则就只能通过反射调用其方法了
IAccess access = null;
try {
access = (IAccess) clas.newInstance();
} catch (IllegalAccessException ex) {
ex.printStackTrace(System.err);
System.exit(1);
} catch (InstantiationException ex) {
ex.printStackTrace(System.err);
System.exit(1);
}
List<String> list = new java.util.ArrayList<String>();
list.add("This is a String ");
// 通过借口的引用调用我们动态生成的类的
access.Calculate(list);
}
private static Class createExecClass() throws Exception {
// ClassPool是javasssit中的类,作用嘛,自描述的
ClassPool pool = ClassPool.getDefault();
CtClass clas = null;
if (!isFirst) {
// 如果不是第一次,就必须要将原来的类在pool中作废,
// 否则无法在同一个pool中生成新的类
// 具体做法是否正确,有待进一步考证
pool = ClassPool.getDefault();
clas = pool.get("ExecVar");
if (clas.isFrozen()) {
System.out
.println("Class ExecVar is frozen,Please looking for something that can make it unfrozen!");
// clas.defrost();
//从pool中拆离这个类
clas.detach();
}
}
isFirst = false;
// 动态生成一个类
clas = pool.makeClass("ExecVar");
clas.addInterface(pool.get("IAccess"));
// 增加默认的构造方法
CtConstructor cons = new CtConstructor(NO_ARGS, clas);
cons.setBody(";");
clas.addConstructor(cons);
// 增加一个方法,参数为java.util.List,返回值是空,名字叫做Calculate,所属类是clas
CtMethod meth = new CtMethod(CtClass.voidType, "Calculate",
new CtClass[] { pool.get("java.util.List") }, clas);
// 用这个随机数检测是否是新生成的类
double random = Math.random();
meth.setBody("System.out.println("这是一个reload测试:"+$1.get(0)+",随机数:"
+ random + "");");
// 将方法加入其中
clas.addMethod(meth);
// 创建一个新的ClassLoader,以加载新的生成的类,旧的s_classLoader连同旧的
// ExecVar类一起被废弃
s_classLoader = new DirectLoader();
return s_classLoader.load("ExecVar", clas.toBytecode());
}
}