使用字节码处理框架javassist动态注入代码

时间:2021-03-20 16:37:29

官方主页:http://www.csg.is.titech.ac.jp/~chiba/javassist/

JavassistCglib等是一些代码增强工具,在运行时刻进行Java字节码增强,虽然速度上稍微慢一点点,但是带来的是代码的简洁,今天用Javassist进行代码增强。

代码:

TestBean.java

  1. package javassist.sample;
  2. public abstract class TestBean {
  3.     public String field;
  4.     public abstract String getM();
  5.     public abstract void setF(String f);
  6.     public String getF() {
  7.         return this.field;
  8.     }
  9. }

TestByteCode.java

  1. package javassist.sample;
  2. import javassist.ClassPool;
  3. import javassist.CtClass;
  4. import javassist.CtConstructor;
  5. import javassist.CtMethod;
  6. public class TestByteCode {
  7.     public static void main(String[] args) throws Exception {
  8.         
  9.         ClassPool pool = ClassPool.getDefault();
  10.         CtClass pt = pool.makeClass("asdf", pool.get("javassist.sample.TestBean"));
  11.         CtMethod method1 = new CtMethod(pool.get("java.lang.String"), "getM"null, pt);
  12.         method1.setBody("{return /"你好/";}");
  13.         pt.addMethod(method1);
  14.         CtConstructor cc = new CtConstructor(null, pt);
  15.         cc.setBody("this.field=/"why?/";");
  16.         pt.addConstructor(cc);
  17.         CtMethod method2 = new CtMethod(CtClass.voidType, "setF",
  18.                 new CtClass[] { pool.get("java.lang.String") }, pt);
  19.         method2.setBody("{this.field=$1;}");
  20.         pt.addMethod(method2);
  21.         Class<?> c = pt.toClass();
  22.         TestBean bean = (TestBean) c.newInstance();
  23.         System.out.println(bean.getM());
  24.         System.out.println(bean.getF());
  25.         bean.setF("setf");
  26.         System.out.println(bean.getF());
  27.     }
  28. }

输出为:

你好

why?

setf

可以看到实现了动态的构造,动态实现抽象函数。在进行代码自动化项目中,能够使用字节码增强将大大提高代码的质量,和减少代码的数量。

 

再看一个例子:

A.java

  1. package javassist.demo;
  2. public class A {
  3.     public void method() {
  4.         for (int i = 0; i < 1000000; i++) {
  5.         }
  6.         System.out.println("method1");
  7.     }
  8. }

JavassistTest.java

  1. package javassist.demo;
  2. import javassist.ClassPool;
  3. import javassist.CtClass;
  4. import javassist.CtMethod;
  5. import javassist.CtNewMethod;
  6. public class JavassistTest {
  7.     public static void main(String[] args) throws Exception {
  8.         // 用于取得字节码类,必须在当前的classpath中,使用全称
  9.         CtClass ctClass = ClassPool.getDefault().get("javassist.demo.A");
  10.         // 需要修改的方法名称
  11.         String mname = "method";
  12.         CtMethod mold = ctClass.getDeclaredMethod(mname);
  13.         // 修改原有的方法名称
  14.         String nname = mname + "$impl";
  15.         mold.setName(nname);
  16.         // 创建新的方法,复制原来的方法
  17.         CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
  18.         // 主要的注入代码
  19.         StringBuffer body = new StringBuffer();
  20.         body.append("{/nlong start = System.currentTimeMillis();/n");
  21.         // 调用原有代码,类似于method();($$)表示所有的参数
  22.         body.append(nname + "($$);/n");
  23.         body.append("System.out.println(/"Call to method " + mname
  24.                 + " took /" +/n (System.currentTimeMillis()-start) + "
  25.                 + "/" ms./");/n");
  26.         body.append("}");
  27.         // 替换新方法
  28.         mnew.setBody(body.toString());
  29.         // 增加新方法
  30.         ctClass.addMethod(mnew);
  31.         // 类已经更改,注意不能使用A a=new A();,因为在同一个classloader中,不允许装载同一个类两次
  32.         A a = (A) ctClass.toClass().newInstance();
  33.         a.method();
  34.     }
  35. }

资料:

http://tsaijun.spaces.live.com/blog/cns!C9F557E0FD3838C1!121.entry

http://www.blogjava.net/zyl/archive/2007/02/10/99171.html

http://www.ibm.com/developerworks/cn/java/j-dyn0916/ 

相关文章