Nutz:車輪を再発明:自分で手を出して、バイトコードツールでAopブロッカーを作ります
Aopは聞きすぎて、Aopを使ったJEerも少なくありません.Spring Aop、AspectJなどです.
角度を変えて、どうして自分でAopブロッカーを書かないのですか?車輪を再発明したらどうですか.
今最も基本的なバイトのコードのツールでDIYの1つのAopを使います.反射であまりに気品がなくて、あれ、谁ができませんか?!
効果のプレビュー:
既存のクラス
改造後
ここでMethodInterceptorはメソッドブロッカーインタフェースです.
はい、着工します.
実装手順は、インタフェースで表現しましょう.
まず、クラスAop 1$$Aopを新規作成します.ちょっと待ってください.バイトコードツールでクラスを新規作成しますよ.eclipseのnew classダイアログを開かないでください.
asmサンプルコード:
中でもmyNameは「Aop 1$$Aop」、enhancedSuperNameは親の名前です
第2歩、フィールドを挿入します(改造後のクラスを思い出しますが、静的プライベートフィールドが2つありますよ)
asmサンプルコード:
ステップ3では、親の構造方法(privateの構造方法を除く)を継承し、親の構造方法が投げ出した異常を追加することを覚えています.
asmサンプルコード:
ステップ4で、Aopテンプレートメソッドを挿入します(いくつかのprivateの_Nut_の先頭のメソッドです)
asmサンプルコード:
例えば挿入_Nut_whenError
ステップ5、メインイベント、オーバーライドにはAopブロックが必要です.
まず、最初の難題を処理する必要があります:戻り値の有無
戻り値のない方法では、ソリューションは比較的簡単です.
asmサンプルコード:
戻り値のあるメソッドについては、3つのケースに分けて、Object(このようなメソッドpublic Object dz()のみを指す)を返し、基本データ型を返し、他のオブジェクト(配列、文字列、列挙、インタフェース、除算など)を返します.
Object以外のオブジェクト)
戻り値がObjectの場合、_Nut_afterの戻り値はそのまま戻せばよく、異常などを投げ出すとnullを返す
戻り値が基本データ型の場合、int/long/doubleなど、Nut_afterの戻り値はパケットを解き,異常などを投げ出すと0/falseを返す.
戻り値が他のオブジェクトの場合、_Nut_afterの戻り値はcheck castする、強転すればよい.
パッケージ解除のコード:
ところで、もう一つの難点は、処理方法のパラメータリストです.Nut_XXXはObject...args、すなわちObject[]を受け入れる.
問題が来ました.もしパラメータが基本タイプだったら?はい、それではかばんを閉めなければなりません.
パッケージのコード:
パラメータはObject[]のコードに変わります.
ここでvisitXは
最後に、完成!!
具体的な実装(asmベース):[url]http://code.google.com/p/nutzlab/source/browse/#svn/trunk/Nutz.Aop-ASM/Nutz.Aop-ASM[/curl
または別のブログを参照してください.
http://wendal.iteye.com/blog/543681
再度、NutzコミュニティとPeterの支持に感谢して、あなた达がなければこの博文がありません.O(∩∩)Oはははは~
Nutz:
http://code.google.com/p/nutz/
角度を変えて、どうして自分でAopブロッカーを書かないのですか?車輪を再発明したらどうですか.
今最も基本的なバイトのコードのツールでDIYの1つのAopを使います.反射であまりに気品がなくて、あれ、谁ができませんか?!
効果のプレビュー:
既存のクラス
public class Aop1{
public void doSomething() throws Throwable{
// , !
}
}
改造後
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はメソッドブロッカーインタフェースです.
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);
}
はい、着工します.
実装手順は、インタフェースで表現しましょう.
public interface ClassEnhander{
void addFields();
void addConstructors();
void addAopMethods();
void enhandMethod();
}
まず、クラスAop 1$$Aopを新規作成します.ちょっと待ってください.バイトコードツールでクラスを新規作成しますよ.eclipseのnew classダイアログを開かないでください.
asmサンプルコード:
ClassWriter cw= new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_6, ACC_PUBLIC, myName, "", enhancedSuperName, new String[]{});
中でもmyNameは「Aop 1$$Aop」、enhancedSuperNameは親の名前です
第2歩、フィールドを挿入します(改造後のクラスを思い出しますが、静的プライベートフィールドが2つありますよ)
asmサンプルコード:
FieldVisitor fv = cv.visitField(ACC_PRIVATE + ACC_STATIC, "_$$Nut_methodArray", "[Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
FieldVisitor fv = cv.visitField(ACC_PRIVATE + ACC_STATIC, "_$$Nut_methodInterceptorList", "[Ljava/util/List;",
"[Ljava/util/List<Lorg/nutz/aop/MethodInterceptor;>;", null);
fv.visitEnd();
ステップ3では、親の構造方法(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();
ステップ4で、Aopテンプレートメソッドを挿入します(いくつかのprivateの_Nut_の先頭のメソッドです)
asmサンプルコード:
例えば挿入_Nut_whenError
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();
ステップ5、メインイベント、オーバーライドにはAopブロックが必要です.
まず、最初の難題を処理する必要があります:戻り値の有無
戻り値のない方法では、ソリューションは比較的簡単です.
asmサンプルコード:
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();
戻り値のあるメソッドについては、3つのケースに分けて、Object(このようなメソッドpublic Object dz()のみを指す)を返し、基本データ型を返し、他のオブジェクト(配列、文字列、列挙、インタフェース、除算など)を返します.
Object以外のオブジェクト)
戻り値がObjectの場合、_Nut_afterの戻り値はそのまま戻せばよく、異常などを投げ出すとnullを返す
戻り値が基本データ型の場合、int/long/doubleなど、Nut_afterの戻り値はパケットを解き,異常などを投げ出すと0/falseを返す.
戻り値が他のオブジェクトの場合、_Nut_afterの戻り値はcheck castする、強転すればよい.
パッケージ解除のコード:
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[]を受け入れる.
問題が来ました.もしパラメータが基本タイプだったら?はい、それではかばんを閉めなければなりません.
パッケージのコード:
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[]のコードに変わります.
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は
void visitX(int i){
if(i < 6){
mv.visitInsn(i + ICONST_0);
}else{
mv.visitIntInsn(BIPUSH, i);
}
}
最後に、完成!!
具体的な実装(asmベース):[url]http://code.google.com/p/nutzlab/source/browse/#svn/trunk/Nutz.Aop-ASM/Nutz.Aop-ASM[/curl
または別のブログを参照してください.
http://wendal.iteye.com/blog/543681
再度、NutzコミュニティとPeterの支持に感谢して、あなた达がなければこの博文がありません.O(∩∩)Oはははは~
Nutz:
http://code.google.com/p/nutz/