ASM

原有的類 
java

Java代碼  數組

  1. public class Aop1{  eclipse

  2.   

  3.     public void doSomething() throws Throwable{  ide

  4.         //打我啊,拍我啊!  函數

  5.     }  工具

  6.   

  7. }  this



改造後 

spa

Java代碼  code

  1. public class Aop1$$Aop extend Aop1{  對象

  2.   

  3.     @Override  

  4.     public void doSomething() throws Throwable{  

  5.     try {  

  6.         if (_Nut_before(188)) {  

  7.             super.doSomething();  

  8.         }  

  9.         _Nut_after(188null);  

  10.     } catch (Exception e) {  

  11.         if(_Nut_Exception(188, e))  

  12.             throw e;  

  13.     } catch (Throwable e) {  

  14.         if(_Nut_Error(188, e))  

  15.             throw e;  

  16.     }  

  17.     }  

  18.   

  19.     private static Method[] _$$Nut_methodArray;  

  20.   

  21.     private static List<MethodInterceptor>[] _$$Nut_methodInterceptorList;  

  22.   

  23.     private boolean _Nut_before(int flag_int, Object... args) {  

  24.     Method method = _$$Nut_methodArray[flag_int];  

  25.     List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];  

  26.     boolean flag = true;  

  27.     for (MethodInterceptor methodInterceptor : miList)  

  28.         flag &= methodInterceptor.beforeInvoke(this, method, args);  

  29.     return flag;  

  30.     }  

  31.   

  32.     private Object _Nut_after(int flag_int, Object src_return, Object... args) {  

  33.     Method method = _$$Nut_methodArray[flag_int];  

  34.     List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];  

  35.     for (MethodInterceptor methodInterceptor : miList)  

  36.         src_return = methodInterceptor.afterInvoke(this, src_return, method, args);  

  37.     return src_return;  

  38.     }  

  39.   

  40.     private boolean _Nut_Exception(int flag_int, Exception e, Object... args) {  

  41.     Method method = _$$Nut_methodArray[flag_int];  

  42.     List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];  

  43.     boolean flag = true;  

  44.     for (MethodInterceptor methodInterceptor : miList)  

  45.         flag &= methodInterceptor.whenException(e, this, method, args);  

  46.     return flag;  

  47.     }  

  48.   

  49.     private boolean _Nut_Error(int flag_int, Throwable e, Object... args) {  

  50.     Method method = _$$Nut_methodArray[flag_int];  

  51.     List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];  

  52.     boolean flag = true;  

  53.     for (MethodInterceptor methodInterceptor : miList)  

  54.         flag &= methodInterceptor.whenError(e, this, method, args);  

  55.     return flag;  

  56.     }  

  57.   

  58. }  



其中MethodInterceptor是方法攔截器接口: 

Java代碼  

  1. public interface MethodInterceptor {  

  2.   

  3.     /** 

  4.      * 在被攔截方法調用以前,將調用該方法。 你可用經過這個方法的返回值,來控制是否真正的調用"被攔截方法"。 

  5.      *  

  6.      * @param obj 

  7.      *            被調用實例 

  8.      * @param method 

  9.      *            實例被調用方法 

  10.      * @param args 

  11.      *            被調用方法所需參數 

  12.      * @return true,繼續調用被攔截方法。false 將不會調用被攔截方法 

  13.      */  

  14.     boolean beforeInvoke(Object obj, Method method, Object... args);  

  15.   

  16.     /** 

  17.      * 你能夠經過這個函數,修改被攔截方法的返回值。默認的,你直接將 returnObj 返回便可 

  18.      *  

  19.      * @param obj 

  20.      *            被調用實例 

  21.      * @param returnObj 

  22.      *            實例被調用方法的返回 

  23.      * @param method 

  24.      *            實例被調用方法 

  25.      * @param args 

  26.      *            被調用方法所需參數 

  27.      * @return 調用者真正將拿到的對象。 若是,你返回的對象類型是錯誤的,好比調用者但願獲得一個 long, 可是,你攔截了這個方法,並返回一個 

  28.      *         String,那麼將發生一個類型轉換的錯誤 

  29.      */  

  30.     Object afterInvoke(Object obj, Object returnObj, Method method, Object... args);  

  31.   

  32.     /** 

  33.      * 當被攔截方法發生異常(Exception),這個方法會被調用。 

  34.      *  

  35.      * @param e 

  36.      *            異常 

  37.      * @param obj 

  38.      *            被調用實例 

  39.      * @param method 

  40.      *            被調用方法 

  41.      * @param args 

  42.      *            被調用方法所需參數 

  43.      *  

  44.      * @return 是否繼續拋出異常 

  45.      */  

  46.     boolean whenException(Exception e, Object obj, Method method, Object... args);  

  47.   

  48.     /** 

  49.      * 當被攔截方法發生錯誤(Error),這個方法會被調用。 

  50.      *  

  51.      * @param e 

  52.      *            錯誤 

  53.      * @param obj 

  54.      *            被調用實例 

  55.      * @param method 

  56.      *            被調用方法 

  57.      * @param args 

  58.      *            被調用方法所需參數 

  59.      *  

  60.      * @return 是否繼續拋出錯誤 

  61.      */  

  62.     boolean whenError(Throwable e, Object obj, Method method, Object... args);  

  63.   

  64. }  



好,開工! 

實現步驟,用個接口來表達吧: 

Java代碼  

  1. public interface ClassEnhander{  

  2.   

  3.     void addFields();  

  4.     void addConstructors();  

  5.     void addAopMethods();  

  6.     void enhandMethod();  

  7. }  



第一步,新建一個類Aop1$$Aop, 等一下, 是用你的字節碼工具新建一個類哦,可不要打開eclipse的new class對話框了 
asm示例代碼: 

Java代碼  

  1. ClassWriter cw= new ClassWriter(ClassWriter.COMPUTE_MAXS);  

  2. cw.visit(V1_6, ACC_PUBLIC, myName, "", enhancedSuperName, new String[]{});  



其中,myName就是"Aop1$$Aop", enhancedSuperName就是父類的名字 

第二步, 插入字段(回想一下改造後的類,但是有兩個靜態私有字段的哦) 
asm示例代碼: 

Java代碼  

  1. FieldVisitor fv = cv.visitField(ACC_PRIVATE + ACC_STATIC, "_$$Nut_methodArray""[Ljava/lang/reflect/Method;"nullnull);  

  2. fv.visitEnd();  



Java代碼  

  1. FieldVisitor fv = cv.visitField(ACC_PRIVATE + ACC_STATIC, "_$$Nut_methodInterceptorList""[Ljava/util/List;",   

  2. "[Ljava/util/List<Lorg/nutz/aop/MethodInterceptor;>;"null);  

  3. fv.visitEnd();  



第三步,繼承父類的構造方法(除了private的構造方法),記得添加父類構造方法拋出的異常哦: 
asm示例代碼: 

Java代碼  收藏代碼

  1. MethodVisitor mv = cw.visitMethod(access, "<init>", desc,null, expClasses);  

  2. mv.visitCode();  

  3. mv.visitVarInsn(ALOAD, 0);  

  4. loadArgs();  

  5. mv.visitMethodInsn(INVOKESPECIAL, superClassName, "<init>", desc);  

  6. mv.visitInsn(RETURN);  

  7. mv.visitMaxs(22);  

  8. mv.visitEnd();  



第四步,插入Aop模板方法(就是那幾個private的_Nut_開頭的方法) 
asm示例代碼: 
例如插入_Nut_whenError 

Java代碼  

  1. MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_VARARGS, "_Nut_Error""(ILjava/lang/Throwable;[Ljava/lang/Object;)Z"nullnull);  

  2. mv.visitCode();  

  3. mv.visitFieldInsn(GETSTATIC, _Nut_myName, "_$$Nut_methodArray""[Ljava/lang/reflect/Method;");  

  4. mv.visitVarInsn(ILOAD, 1);  

  5. mv.visitInsn(AALOAD);  

  6. mv.visitVarInsn(ASTORE, 4);  

  7. mv.visitFieldInsn(GETSTATIC, _Nut_myName, "_$$Nut_methodInterceptorList""[Ljava/util/List;");  

  8. mv.visitVarInsn(ILOAD, 1);  

  9. mv.visitInsn(AALOAD);  

  10. mv.visitVarInsn(ASTORE, 5);  

  11. mv.visitInsn(ICONST_1);  

  12. mv.visitVarInsn(ISTORE, 6);  

  13. mv.visitVarInsn(ALOAD, 5);  

  14. mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List""iterator""()Ljava/util/Iterator;");  

  15. mv.visitVarInsn(ASTORE, 8);  

  16. Label l0 = new Label();  

  17. mv.visitJumpInsn(GOTO, l0);  

  18. Label l1 = new Label();  

  19. mv.visitLabel(l1);  

  20. mv.visitFrame(Opcodes.F_FULL, 9new Object[] {_Nut_myName, Opcodes.INTEGER, "java/lang/Throwable""[Ljava/lang/Object;",   

  21.   

  22. "java/lang/reflect/Method""java/util/List", Opcodes.INTEGER, Opcodes.TOP, "java/util/Iterator"}, 0new Object[] {});  

  23. mv.visitVarInsn(ALOAD, 8);  

  24. mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator""next""()Ljava/lang/Object;");  

  25. mv.visitTypeInsn(CHECKCAST, "org/nutz/aop/MethodInterceptor");  

  26. mv.visitVarInsn(ASTORE, 7);  

  27. mv.visitVarInsn(ILOAD, 6);  

  28. mv.visitVarInsn(ALOAD, 7);  

  29. mv.visitVarInsn(ALOAD, 2);  

  30. mv.visitVarInsn(ALOAD, 0);  

  31. mv.visitVarInsn(ALOAD, 4);  

  32. mv.visitVarInsn(ALOAD, 3);  

  33. mv.visitMethodInsn(INVOKEINTERFACE, "org/nutz/aop/MethodInterceptor""whenError",   

  34.   

  35. "(Ljava/lang/Throwable;Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Z");  

  36. mv.visitInsn(IAND);  

  37. mv.visitVarInsn(ISTORE, 6);  

  38. mv.visitLabel(l0);  

  39. mv.visitFrame(Opcodes.F_SAME, 0null0null);  

  40. mv.visitVarInsn(ALOAD, 8);  

  41. mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator""hasNext""()Z");  

  42. mv.visitJumpInsn(IFNE, l1);  

  43. mv.visitVarInsn(ILOAD, 6);  

  44. mv.visitInsn(IRETURN);  

  45. mv.visitMaxs(69);  

  46. mv.visitEnd();  



第五步,重頭戲,覆寫須要Aop攔截的方法: 
首先,須要處理第一個難題: 有無返回值 
對於無返回值的方法,解決方案比較簡單: 
asm示例代碼: 

Java代碼  

  1. MethodVisitor mv = cw.visitMethod(methodAccess, methodName,methodDesc,null, convertExp(method.getExceptionTypes()));  

  2. mv.visitCode();  

  3. Label l0 = new Label();  

  4. Label l1 = new Label();  

  5. Label l2 = new Label();  

  6.         mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");  

  7.         Label l3 = new Label();  

  8.         mv.visitTryCatchBlock(l0, l1, l3, "java/lang/Throwable");  

  9.         mv.visitLabel(l0);  

  10.         mv.visitVarInsn(ALOAD, 0);  

  11.         mv.visitIntInsn(SIPUSH, methodIndex);  

  12.         loadArgsAsArray();  

  13.         mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_before""(I[Ljava/lang/Object;)Z");  

  14.         Label l4 = new Label();  

  15.         mv.visitJumpInsn(IFEQ, l4);  

  16.         mv.visitVarInsn(ALOAD, 0);  

  17.         loadArgs();  

  18.         mv.visitMethodInsn(INVOKESPECIAL, enhancedSuperName, methodName, desc);  

  19.         mv.visitLabel(l4);  

  20.         mv.visitFrame(Opcodes.F_SAME, 0null0null);  

  21.         mv.visitVarInsn(ALOAD, 0);  

  22.         mv.visitIntInsn(SIPUSH, methodIndex);  

  23.         mv.visitInsn(ACONST_NULL);  

  24.         loadArgsAsArray();  

  25.         mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_after""(ILjava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");  

  26.         mv.visitInsn(POP);  

  27.         mv.visitLabel(l1);  

  28.         Label l5 = new Label();  

  29.         mv.visitJumpInsn(GOTO, l5);  

  30.         mv.visitLabel(l2);  

  31.         mv.visitFrame(Opcodes.F_SAME1, 0null1new Object[] {"java/lang/Exception"});  

  32.         mv.visitVarInsn(ASTORE, lastIndex);  

  33.         mv.visitVarInsn(ALOAD, 0);  

  34.         mv.visitIntInsn(SIPUSH, methodIndex);  

  35.         mv.visitVarInsn(ALOAD, lastIndex);  

  36.         loadArgsAsArray();  

  37.         mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_Exception""(ILjava/lang/Exception;[Ljava/lang/Object;)Z");  

  38.         mv.visitJumpInsn(IFEQ, l5);  

  39.         mv.visitVarInsn(ALOAD, lastIndex);  

  40.         mv.visitInsn(ATHROW);  

  41.         mv.visitLabel(l3);  

  42.         mv.visitFrame(Opcodes.F_SAME1, 0null1new Object[] {"java/lang/Throwable"});  

  43.         mv.visitVarInsn(ASTORE, lastIndex);  

  44.         mv.visitVarInsn(ALOAD, 0);  

  45.         mv.visitIntInsn(SIPUSH, methodIndex);  

  46.         mv.visitVarInsn(ALOAD, lastIndex);  

  47.         loadArgsAsArray();  

  48.         mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_Error""(ILjava/lang/Throwable;[Ljava/lang/Object;)Z");  

  49.         mv.visitJumpInsn(IFEQ, l5);  

  50.         mv.visitVarInsn(ALOAD, lastIndex);  

  51.         mv.visitInsn(ATHROW);  

  52.         mv.visitLabel(l5);  

  53.         mv.visitFrame(Opcodes.F_SAME, 0null0null);  

  54.         mv.visitInsn(RETURN);  

  55.         mv.visitMaxs(11); // 自動計算  

  56.         mv.visitEnd();  



對於有返回值的方法,分三種狀況, 返回Object(單單指這類方法 public Object dz()),返回基本數據類型,返回其餘對象(如返回數組,字符串,枚舉,接口,除 

Object外的對象) 
返回值爲Object時,把_Nut_after的返回值直接返回便可,當拋出異常等,則返回null 
返回值爲基本數據類型時,如int/long/double,把_Nut_after的返回值解包,當拋出異常等,則返回0/false 
返回值爲其餘對象時, 把_Nut_after的返回值 check cast一下,進行強轉便可. 

解包的代碼: 

Java代碼  

  1. if(type.equals(Type.BOOLEAN_TYPE)){  

  2.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean""booleanValue""()Z");  

  3.         }else if(type.equals(Type.BYTE_TYPE)){  

  4.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte""byteValue""()B");  

  5.         }else if(type.equals(Type.CHAR_TYPE)){  

  6.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character""charValue""()C");  

  7.         }else if(type.equals(Type.SHORT_TYPE)){  

  8.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short""shortValue""()S");  

  9.         }else if(type.equals(Type.INT_TYPE)){  

  10.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer""intValue""()I");  

  11.         }else if(type.equals(Type.LONG_TYPE)){  

  12.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long""longValue""()J");  

  13.         }else if(type.equals(Type.FLOAT_TYPE)){  

  14.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float""floatValue""()F");  

  15.         }else if(type.equals(Type.DOUBLE_TYPE)){  

  16.             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double""doubleValue""()D");  

  17.         }  



對了對了, 還有一個難點, 處理方法的參數列表,因爲_Nut_XXX接受的是Object...args,即一個Object[]. 
問題就來了, 若是其中某個參數是基本類型呢?恩, 那就得封包了 

封包的代碼: 

Java代碼  

  1. if(type.equals(Type.BOOLEAN_TYPE)){  

  2.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean""valueOf""(Z)Ljava/lang/Boolean;");  

  3.         }else if(type.equals(Type.BYTE_TYPE)){  

  4.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte""valueOf""(B)Ljava/lang/Byte;");  

  5.         }else if(type.equals(Type.CHAR_TYPE)){  

  6.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character""valueOf""(C)Ljava/lang/Character;");  

  7.         }else if(type.equals(Type.SHORT_TYPE)){  

  8.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short""valueOf""(S)Ljava/lang/Short;");  

  9.         }else if(type.equals(Type.INT_TYPE)){  

  10.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer""valueOf""(I)Ljava/lang/Integer;");  

  11.         }else if(type.equals(Type.LONG_TYPE)){  

  12.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long""valueOf""(J)Ljava/lang/Long;");  

  13.         }else if(type.equals(Type.FLOAT_TYPE)){  

  14.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float""valueOf""(F)Ljava/lang/Float;");  

  15.         }else if(type.equals(Type.DOUBLE_TYPE)){  

  16.             mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double""valueOf""(D)Ljava/lang/Double;");  

  17.         }  



參數轉爲Object[]的代碼: 

Java代碼  

  1. int index = getArgIndex(0);  

  2.         for (int i = 0; i < argumentTypes.length; i++) {  

  3.             mv.visitInsn(DUP);  

  4.             visitX(i);  

  5.             Type t = argumentTypes[i];  

  6.             loadInsn(t, index);  

  7.             index += t.getSize();  

  8.             packagePrivateData(t);  

  9.             mv.visitInsn(AASTORE);  

  10.         }  


其中visitX爲 

Java代碼  

  1. void visitX(int i){  

  2.     if(i < 6){  

  3.         mv.visitInsn(i + ICONST_0);  

  4.     }else{  

  5.         mv.visitIntInsn(BIPUSH, i);  

  6.     }  

  7. }  

相關文章
相關標籤/搜索