原有的類
java
Java代碼 數組
public class Aop1{ eclipse
public void doSomething() throws Throwable{ ide
//打我啊,拍我啊! 函數
} 工具
} this
改造後
spa
Java代碼 code
public class Aop1$$Aop extend Aop1{ 對象
@Override
public void doSomething() throws Throwable{
try {
if (_Nut_before(188)) {
super.doSomething();
}
_Nut_after(188, null);
} catch (Exception e) {
if(_Nut_Exception(188, e))
throw e;
} catch (Throwable e) {
if(_Nut_Error(188, e))
throw e;
}
}
private static Method[] _$$Nut_methodArray;
private static List<MethodInterceptor>[] _$$Nut_methodInterceptorList;
private boolean _Nut_before(int flag_int, Object... args) {
Method method = _$$Nut_methodArray[flag_int];
List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];
boolean flag = true;
for (MethodInterceptor methodInterceptor : miList)
flag &= methodInterceptor.beforeInvoke(this, method, args);
return flag;
}
private Object _Nut_after(int flag_int, Object src_return, Object... args) {
Method method = _$$Nut_methodArray[flag_int];
List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];
for (MethodInterceptor methodInterceptor : miList)
src_return = methodInterceptor.afterInvoke(this, src_return, method, args);
return src_return;
}
private boolean _Nut_Exception(int flag_int, Exception e, Object... args) {
Method method = _$$Nut_methodArray[flag_int];
List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];
boolean flag = true;
for (MethodInterceptor methodInterceptor : miList)
flag &= methodInterceptor.whenException(e, this, method, args);
return flag;
}
private boolean _Nut_Error(int flag_int, Throwable e, Object... args) {
Method method = _$$Nut_methodArray[flag_int];
List<MethodInterceptor> miList = _$$Nut_methodInterceptorList[flag_int];
boolean flag = true;
for (MethodInterceptor methodInterceptor : miList)
flag &= methodInterceptor.whenError(e, this, method, args);
return flag;
}
}
其中MethodInterceptor是方法攔截器接口:
Java代碼
public interface MethodInterceptor {
/**
* 在被攔截方法調用以前,將調用該方法。 你可用經過這個方法的返回值,來控制是否真正的調用"被攔截方法"。
*
* @param obj
* 被調用實例
* @param method
* 實例被調用方法
* @param args
* 被調用方法所需參數
* @return true,繼續調用被攔截方法。false 將不會調用被攔截方法
*/
boolean beforeInvoke(Object obj, Method method, Object... args);
/**
* 你能夠經過這個函數,修改被攔截方法的返回值。默認的,你直接將 returnObj 返回便可
*
* @param obj
* 被調用實例
* @param returnObj
* 實例被調用方法的返回
* @param method
* 實例被調用方法
* @param args
* 被調用方法所需參數
* @return 調用者真正將拿到的對象。 若是,你返回的對象類型是錯誤的,好比調用者但願獲得一個 long, 可是,你攔截了這個方法,並返回一個
* String,那麼將發生一個類型轉換的錯誤
*/
Object afterInvoke(Object obj, Object returnObj, Method method, Object... args);
/**
* 當被攔截方法發生異常(Exception),這個方法會被調用。
*
* @param e
* 異常
* @param obj
* 被調用實例
* @param method
* 被調用方法
* @param args
* 被調用方法所需參數
*
* @return 是否繼續拋出異常
*/
boolean whenException(Exception e, Object obj, Method method, Object... args);
/**
* 當被攔截方法發生錯誤(Error),這個方法會被調用。
*
* @param e
* 錯誤
* @param obj
* 被調用實例
* @param method
* 被調用方法
* @param args
* 被調用方法所需參數
*
* @return 是否繼續拋出錯誤
*/
boolean whenError(Throwable e, Object obj, Method method, Object... args);
}
好,開工!
實現步驟,用個接口來表達吧:
Java代碼
public interface ClassEnhander{
void addFields();
void addConstructors();
void addAopMethods();
void enhandMethod();
}
第一步,新建一個類Aop1$$Aop, 等一下, 是用你的字節碼工具新建一個類哦,可不要打開eclipse的new class對話框了
asm示例代碼:
Java代碼
ClassWriter cw= new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_6, ACC_PUBLIC, myName, "", enhancedSuperName, new String[]{});
其中,myName就是"Aop1$$Aop", enhancedSuperName就是父類的名字
第二步, 插入字段(回想一下改造後的類,但是有兩個靜態私有字段的哦)
asm示例代碼:
Java代碼
FieldVisitor fv = cv.visitField(ACC_PRIVATE + ACC_STATIC, "_$$Nut_methodArray", "[Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
Java代碼
FieldVisitor fv = cv.visitField(ACC_PRIVATE + ACC_STATIC, "_$$Nut_methodInterceptorList", "[Ljava/util/List;",
"[Ljava/util/List<Lorg/nutz/aop/MethodInterceptor;>;", null);
fv.visitEnd();
第三步,繼承父類的構造方法(除了private的構造方法),記得添加父類構造方法拋出的異常哦:
asm示例代碼:
MethodVisitor mv = cw.visitMethod(access, "<init>", desc,null, expClasses);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
loadArgs();
mv.visitMethodInsn(INVOKESPECIAL, superClassName, "<init>", desc);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
第四步,插入Aop模板方法(就是那幾個private的_Nut_開頭的方法)
asm示例代碼:
例如插入_Nut_whenError
Java代碼
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_VARARGS, "_Nut_Error", "(ILjava/lang/Throwable;[Ljava/lang/Object;)Z", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, _Nut_myName, "_$$Nut_methodArray", "[Ljava/lang/reflect/Method;");
mv.visitVarInsn(ILOAD, 1);
mv.visitInsn(AALOAD);
mv.visitVarInsn(ASTORE, 4);
mv.visitFieldInsn(GETSTATIC, _Nut_myName, "_$$Nut_methodInterceptorList", "[Ljava/util/List;");
mv.visitVarInsn(ILOAD, 1);
mv.visitInsn(AALOAD);
mv.visitVarInsn(ASTORE, 5);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ISTORE, 6);
mv.visitVarInsn(ALOAD, 5);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, 8);
Label l0 = new Label();
mv.visitJumpInsn(GOTO, l0);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_FULL, 9, new Object[] {_Nut_myName, Opcodes.INTEGER, "java/lang/Throwable", "[Ljava/lang/Object;",
"java/lang/reflect/Method", "java/util/List", Opcodes.INTEGER, Opcodes.TOP, "java/util/Iterator"}, 0, new Object[] {});
mv.visitVarInsn(ALOAD, 8);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, "org/nutz/aop/MethodInterceptor");
mv.visitVarInsn(ASTORE, 7);
mv.visitVarInsn(ILOAD, 6);
mv.visitVarInsn(ALOAD, 7);
mv.visitVarInsn(ALOAD, 2);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 4);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEINTERFACE, "org/nutz/aop/MethodInterceptor", "whenError",
"(Ljava/lang/Throwable;Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Z");
mv.visitInsn(IAND);
mv.visitVarInsn(ISTORE, 6);
mv.visitLabel(l0);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 8);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, l1);
mv.visitVarInsn(ILOAD, 6);
mv.visitInsn(IRETURN);
mv.visitMaxs(6, 9);
mv.visitEnd();
第五步,重頭戲,覆寫須要Aop攔截的方法:
首先,須要處理第一個難題: 有無返回值
對於無返回值的方法,解決方案比較簡單:
asm示例代碼:
Java代碼
MethodVisitor mv = cw.visitMethod(methodAccess, methodName,methodDesc,null, convertExp(method.getExceptionTypes()));
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
Label l3 = new Label();
mv.visitTryCatchBlock(l0, l1, l3, "java/lang/Throwable");
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitIntInsn(SIPUSH, methodIndex);
loadArgsAsArray();
mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_before", "(I[Ljava/lang/Object;)Z");
Label l4 = new Label();
mv.visitJumpInsn(IFEQ, l4);
mv.visitVarInsn(ALOAD, 0);
loadArgs();
mv.visitMethodInsn(INVOKESPECIAL, enhancedSuperName, methodName, desc);
mv.visitLabel(l4);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitIntInsn(SIPUSH, methodIndex);
mv.visitInsn(ACONST_NULL);
loadArgsAsArray();
mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_after", "(ILjava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(POP);
mv.visitLabel(l1);
Label l5 = new Label();
mv.visitJumpInsn(GOTO, l5);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Exception"});
mv.visitVarInsn(ASTORE, lastIndex);
mv.visitVarInsn(ALOAD, 0);
mv.visitIntInsn(SIPUSH, methodIndex);
mv.visitVarInsn(ALOAD, lastIndex);
loadArgsAsArray();
mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_Exception", "(ILjava/lang/Exception;[Ljava/lang/Object;)Z");
mv.visitJumpInsn(IFEQ, l5);
mv.visitVarInsn(ALOAD, lastIndex);
mv.visitInsn(ATHROW);
mv.visitLabel(l3);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
mv.visitVarInsn(ASTORE, lastIndex);
mv.visitVarInsn(ALOAD, 0);
mv.visitIntInsn(SIPUSH, methodIndex);
mv.visitVarInsn(ALOAD, lastIndex);
loadArgsAsArray();
mv.visitMethodInsn(INVOKESPECIAL, myName, "_Nut_Error", "(ILjava/lang/Throwable;[Ljava/lang/Object;)Z");
mv.visitJumpInsn(IFEQ, l5);
mv.visitVarInsn(ALOAD, lastIndex);
mv.visitInsn(ATHROW);
mv.visitLabel(l5);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1); // 自動計算
mv.visitEnd();
對於有返回值的方法,分三種狀況, 返回Object(單單指這類方法 public Object dz()),返回基本數據類型,返回其餘對象(如返回數組,字符串,枚舉,接口,除
Object外的對象)
返回值爲Object時,把_Nut_after的返回值直接返回便可,當拋出異常等,則返回null
返回值爲基本數據類型時,如int/long/double,把_Nut_after的返回值解包,當拋出異常等,則返回0/false
返回值爲其餘對象時, 把_Nut_after的返回值 check cast一下,進行強轉便可.
解包的代碼:
Java代碼
if(type.equals(Type.BOOLEAN_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
}else if(type.equals(Type.BYTE_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
}else if(type.equals(Type.CHAR_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
}else if(type.equals(Type.SHORT_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
}else if(type.equals(Type.INT_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
}else if(type.equals(Type.LONG_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
}else if(type.equals(Type.FLOAT_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
}else if(type.equals(Type.DOUBLE_TYPE)){
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
}
對了對了, 還有一個難點, 處理方法的參數列表,因爲_Nut_XXX接受的是Object...args,即一個Object[].
問題就來了, 若是其中某個參數是基本類型呢?恩, 那就得封包了
封包的代碼:
Java代碼
if(type.equals(Type.BOOLEAN_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
}else if(type.equals(Type.BYTE_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
}else if(type.equals(Type.CHAR_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
}else if(type.equals(Type.SHORT_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
}else if(type.equals(Type.INT_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
}else if(type.equals(Type.LONG_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
}else if(type.equals(Type.FLOAT_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
}else if(type.equals(Type.DOUBLE_TYPE)){
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
}
參數轉爲Object[]的代碼:
Java代碼
int index = getArgIndex(0);
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(DUP);
visitX(i);
Type t = argumentTypes[i];
loadInsn(t, index);
index += t.getSize();
packagePrivateData(t);
mv.visitInsn(AASTORE);
}
其中visitX爲
Java代碼
void visitX(int i){
if(i < 6){
mv.visitInsn(i + ICONST_0);
}else{
mv.visitIntInsn(BIPUSH, i);
}
}