アスペクト志向の実装を動的プロキシ(または、ダイナミックプロキシ)と呼ばれる方法でJDK標準で
提供される機能だけで行うことが出来ます。
実現には
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler
を使用します。
まずは、
java.lang.reflect.InvocationHandler
の実装クラスを用意します。
これは、「呼び出しハンドラ」と呼ばれProxyクラス経由で実行されるメソッドをフックする為に実装します。
public class MyInvocationHandler implements InvocationHandler{
        private Object target;
        public MyInvocationHandler(Object target){
                // 本来のインスタンスを保持しておきます。
                this.target = target;
        }
        @Override
        public Object invoke( Object proxy , Method method , Object[] args ) throws Throwable {
                System.out.println("invoke method:"+method.getName());
                // 本来呼ばれるべきメソッドを本来のインスタンスで実行します。
                // 必要に応じて実行すべきでない場合、実行する必要はありません。
                Object ret = method.invoke( this.target , args );
                System.out.println("invoke result:"+ret.toString());
                // 通常の場合、戻り値は本来呼ばれるべきメソッドの結果を返します。
                // 但し、必要に応じてそうでなくてもかまいません。
                return ret;
        }
}
次に、Proxy経由で実行するクラスを用意します。
このときに注意が必要です。
Proxyクラスの動作の特性上、必ずinterfaceとその実装クラスを用意しなければなりません。
interface MyInterface{
        String sayHello(String name);
}
class MyClass implements MyInterface{
        @Override
        public String sayHello( String name ) {
                // hello! 名前をコンソールに出力するだけ。
                System.out.println("hello! "+name);
                // 戻り値としてhello!を返す。
                return "hello!";
        }
}
最後にProxy経由でインスタンスを生成し、
後は通常の呼び出しと同じようにメソッドを実行します。
public static void main(String[] args) throws Exception{
        // 通常のnewで得られるインスタンスを用意しておきます。
        MyInterface myInterfaceReal = new MyClass();
        // 呼び出しハンドラクラスを生成します。
        // その際に実際のインスタンスを渡しておきます。
        InvocationHandler handler = new MyInvocationHandler(myInterfaceReal);
        // Proxy.newProxyInstanceメソッドを使ってインスタンスを生成します。
        MyInterface myInterface = (MyInterface)Proxy.newProxyInstance(
                                                // ClassLoader
                                                MyClass.class.getClassLoader() ,
                                                // メソッドフックに使われるinterface.
                                                MyClass.class.getInterfaces() ,
                                                // 呼び出しハンドラ
                                                handler );
        // 実行は通常と変わりません。myInterfaceの方であることに注意してください。
        String ret = myInterface.sayHello( "JAVA" );
        System.out.println(ret);
}
Proxyインスタンスの生成などは概ね定型的な処理になります。
最後にこの実行結果は次のようになります。
invoke method:sayHello
hello! JAVA
invoke result:hello!
hello!
0 件のコメント:
コメントを投稿