我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。但反射的性能开销较大,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(); }
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"); }
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++; } } } }