我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。但反射的性能开销较大,Javassit是一个东京研究院开发的第三方库,提供了运行时操作Java字节码的方法。 类似字节码操作方法还有ASM,几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM。
package fanshe;
public class Person extends China implements Student{
private int sex;
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String sayHello(String a,String b) throws Exception{
System.out.println(a+b);
return a+b;
}
public void test(){
for (int i=0;i<1000000;i++) {
}
System.out.println("test");
}
}
【实例1】
private static void timeFun() throws Exception {
CtClass ctClass = ClassPool.getDefault().get("fanshe.Person");
String mname = "test";
CtMethod mold = ctClass.getDeclaredMethod(mname);
//修改原有的方法名称
String nname = mname + "$impl";
mold.setName(nname);
//创建新的方法,复制原来的方法
CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
//主要的注入代码
StringBuffer body = new StringBuffer();
body.append("{\nlong start = System.currentTimeMillis();\n");
//调用原有代码,类似于method();($$)表示所有的参数
body.append(nname + "($$);\n");
body.append("System.out.println(\"Call to method "
+ mname
+ " took \" +\n (System.currentTimeMillis()-start) + "
+ "\" ms.\");\n");
body.append("}");
//替换新方法
mnew.setBody(body.toString());
//增加新方法
ctClass.addMethod(mnew);
//类已经更改,注意不能使用A a=new A();,因为在同一个classloader中,不允许装载同一个类两次
Person a=(Person)ctClass.toClass().newInstance();
a.test();
}
【实例2】
private static void inserFun() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("fanshe.Person");
CtMethod ctm = ctClass.getDeclaredMethod("sayHello");
ctm.insertBefore("{System.out.println(\"i=\"+($1)+\",j=\"+$2);}");
ctClass.writeFile();//这里我不知道为什么写不到文件里,很纳闷。
Method m = ctClass.toClass().getMethod("sayHello",java.lang.String.class,java.lang.String.class);
Person s = new Person();
m.invoke(s, "a","b");
}
补充:可以通过javassist动态获取类方法中的参数列表(反射做不到这一点)
static void javassistGetInfo() throws Exception{
Class<?> clazz = Class.forName("fanshe.Person");
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(clazz.getName());
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method mt:declaredMethods) {
String modifier = Modifier.toString(mt.getModifiers());
Class<?> returnType = mt.getReturnType();
String name = mt.getName();
Class<?>[] parameterTypes = mt.getParameterTypes();
System.out.print("\n"+modifier+" "+returnType.getName()+" "+name+" (");
//CtMethod[] declaredMethods1 = cc.getDeclaredMethods();
CtMethod ctm = cc.getDeclaredMethod(name);
MethodInfo methodInfo = ctm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attribute = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);
int pos = Modifier.isStatic(ctm.getModifiers()) ? 0 : 1;
for (int i=0;i<ctm.getParameterTypes().length;i++) {
System.out.print(parameterTypes[i]+" "+attribute.variableName(i+pos));
if (i<ctm.getParameterTypes().length-1) {
System.out.print(",");
}
}
System.out.print(")");
Class<?>[] exceptionTypes = mt.getExceptionTypes();
if (exceptionTypes.length>0) {
System.out.print(" throws ");
int j=0;
for (Class<?> cl:exceptionTypes) {
System.out.print(cl.getName());
if (j<exceptionTypes.length-1) {
System.out.print(",");
}
j++;
}
}
}
}