Sealyu

          --- 博客已遷移至: http://www.sealyu.com/blog

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            618 隨筆 :: 87 文章 :: 225 評(píng)論 :: 0 Trackbacks
          在jdk5.0版本中,添加了一個(gè)新的接口java.lang.instrument.Instrumentation,這個(gè)接口可以用來(lái)在類加載的時(shí)候 對(duì)類的字節(jié)碼進(jìn)行修改,改變類的功能的實(shí)現(xiàn)。通過(guò)這種方法,我們就可以實(shí)現(xiàn)aop的功能,這是和proxy動(dòng)態(tài)代理的不同的另一種aop的實(shí)現(xiàn)。
               這種方法的原理很簡(jiǎn)單,就是通過(guò)修改編譯器已經(jīng)編譯好的字節(jié)碼來(lái)修改類,和我們通常修改類源碼來(lái)實(shí)現(xiàn)對(duì)類的修改差不多。只不過(guò)是在兩個(gè)不同的層次進(jìn)行的修改。
               一下是使用這個(gè)接口的具體方法。
              第一步:
              需要實(shí)現(xiàn)  java.lang.instrument.Instrumentation這個(gè)接口,通過(guò)在這個(gè)接口的實(shí)現(xiàn)類中,在加載類的時(shí)候修改類的字節(jié)碼。以下是接口的實(shí)現(xiàn) Transformer.java
          public class Transformer implements ClassFileTransformer {
             //在程序運(yùn)行之前就被jvm調(diào)用的方法
             public static void premain(String args, Instrumentation inst) {
                  inst.addTransformer(new Transformer());
              }
              public byte[] transform(ClassLoader loader, String className,
                      Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                      byte[] classfileBuffer) throws IllegalClassFormatException {
                  byte[] result =null; //定義新的字節(jié)碼存儲(chǔ)變量
                  //在此通過(guò)修改類的字節(jié)碼 classfileBuffer,來(lái)更改類。
                  return result; //返回新的字節(jié)碼
              }
          }
          第二步:
          需要將
          Transformer類的class文件生成jar文件,如t.jar,在jar文件的MANIFEST.MF添加 Premain-Class: Transformer 這一項(xiàng)。在運(yùn)行程序時(shí),需要在jvm使用如下參數(shù) -javaagent:t.jar 。這樣jvm在運(yùn)行時(shí),就會(huì)先找到Premain-Class: Transformer  這一項(xiàng)指定的Transformer類的premain方法,運(yùn)行此方法。也就實(shí)現(xiàn)了在以后的類的加載中,對(duì)類的字節(jié)碼進(jìn)行修改的功能。

                  就是使用
          java.lang.instrument.Instrumentation接口的方法。那么如何實(shí)現(xiàn)aop功能呢,這就需要在更改字節(jié)碼的時(shí)候,在類的功能上添加aop方面的功能。
                 現(xiàn)在我們看看如何實(shí)現(xiàn)在方法的調(diào)用前和返回時(shí)打印當(dāng)前時(shí)間的aop功能。
                先更改Transformer的transform方法
          public byte[] transform(ClassLoader loader, String className,
                      Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                      byte[] classfileBuffer) throws IllegalClassFormatException {
                  byte[] result =classfileBuffer; //定義新的字節(jié)碼存儲(chǔ)變量
                  //在此通過(guò)修改類的字節(jié)碼 classfileBuffer,來(lái)更改類。
                  if (loader != ClassLoader.getSystemClassLoader()) {
                      return classfileBuffer;
                  }

                  ClassReader reader = new ClassReader(classfileBuffer);
                  ClassWriter writer = new ClassWriter(true);
                  ClassAdapter adapter = new PerfClassAdapter(writer, className);
                  reader.accept(adapter, true);
                  result = writer.toByteArray();
                  return result; //返回新的字節(jié)碼
              }

          這樣就通過(guò)PerfClassAdapter類來(lái)修改字節(jié)碼.
          PerfClassAdapter.java
          public class PerfClassAdapter extends ClassAdapter {
              private String className;
              public PerfClassAdapter(ClassVisitor visitor, String theClass) {
                  super(visitor);
                  this.className = theClass;
              }
              public MethodVisitor visitMethod(int arg,
                      String name,
                      String descriptor,
                      String signature,
                      String[] exceptions) {
                  MethodVisitor mv = super.visitMethod(arg,
                          name,
                          descriptor,
                          signature,
                          exceptions);
                  MethodAdapter ma = new PerfMethodAdapter(mv, className, name);
                  return ma;
              } 
          }
          PerfMethodAdapter.java
          public class PerfMethodAdapter extends MethodAdapter {
              private String _className, _methodName;
             
              public PerfMethodAdapter(MethodVisitor visitor,
                      String className,
                      String methodName) {
                  super(visitor);
                  _className = className;
                  _methodName = methodName;
              }

              public void visitCode() {
                  this.visitLdcInsn(_className);
                  this.visitLdcInsn(_methodName);
                  this.visitMethodInsn(INVOKESTATIC,
                          "
          AOP_LOG",
                          "start",
                          "(Ljava/lang/String;Ljava/lang/String;)V");
                  super.visitCode();
              }

              public void visitInsn(int inst) {
                  switch (inst) {
                  case Opcodes.ARETURN:
                  case Opcodes.DRETURN:
                  case Opcodes.FRETURN:
                  case Opcodes.IRETURN:
                  case Opcodes.LRETURN:
                  case Opcodes.RETURN:
                  case Opcodes.ATHROW:
                      this.visitLdcInsn(_className);
                      this.visitLdcInsn(_methodName);
                      this.visitMethodInsn(INVOKESTATIC,
                              "AOP_LOG",
                              "end",
                              "(Ljava/lang/String;Ljava/lang/String;)V");
                      break;
                  default:
                      break;
                  }
                  super.visitInsn(inst);
              }
          }
          AOP_LOG.java
          public class
          AOP_LOG{
             
              public static void start(String className, String methodName) {
                  System.out.println(new StringBuilder(className)
                          .append('"t')
                          .append(methodName)
                          .append(""tstart"t")
                          .append(System.currentTimeMillis()));       
              }
              public static void end(String className, String methodName) {
                  System.out.println(new StringBuilder(className)
                          .append('"t')
                          .append(methodName)
                          .append(""tend"t")
                          .append(System.currentTimeMillis()));       
              }
          }
          這些類結(jié)合起來(lái),就實(shí)現(xiàn)了在方法的調(diào)用前,會(huì)調(diào)用
          AOP_LOG類的start方法,在方法返回后,調(diào)用AOP_LOG類的end方法。

          現(xiàn)在寫(xiě)個(gè)測(cè)試類來(lái)測(cè)試一下。
          TSMain.java
          public class TSMain {

              public static void main(String[] args) {
                  TSMain tsm = new TSMain();
                  tsm.printClassName();
              }
             
              public void printClassName(){
                  System.out.println("hello "+this.getClass().getName());
              }
          }

          運(yùn)行命令如下:
          =>java -javaagent:t.jar  classpath cpdir TSMain  #
          cpdir 需要用到的類的路徑

          輸出結(jié)果如下:
          TSMain    main    start    1179374205562
          TSMain    <init>    start    1179374205562
          TSMain    <init>    end    1179374205562
          TSMain    printClassName    start    1179374205562
          hello TSMain
          TSMain    printClassName    end    1179374205562
          TSMain    main    end    1179374205562


          到這里就完成整個(gè)aop的功能實(shí)現(xiàn)了。需要補(bǔ)充說(shuō)明的是,這里修改類的字節(jié)碼是使用了asm的基礎(chǔ)類庫(kù),所以需要導(dǎo)入這些asm的jar文件。
          posted on 2008-04-10 22:30 seal 閱讀(565) 評(píng)論(0)  編輯  收藏 所屬分類: Java基礎(chǔ)
          主站蜘蛛池模板: 怀集县| 金秀| 灌阳县| 项城市| 新蔡县| 永嘉县| 连平县| 甘南县| 江川县| 会宁县| 新邵县| 台中县| 酉阳| 祥云县| 称多县| 遵义县| 酒泉市| 木里| 芦山县| 仙游县| 浙江省| 砀山县| 邯郸县| 西青区| 工布江达县| 林州市| 抚松县| 蓬安县| 苏尼特右旗| 盈江县| 栖霞市| 岗巴县| 甘南县| 老河口市| 同江市| 兖州市| 兰坪| 苗栗县| 莫力| 军事| 南皮县|