Nutz:車輪を再発明:自分で手を出して、バイトコードツールでAopブロッカーを作ります


Aopは聞きすぎて、Aopを使ったJEerも少なくありません.Spring Aop、AspectJなどです.
角度を変えて、どうして自分で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/