challenge メソッドのフック

例えば、あるクラスのあるメソッドを実行する前に他の処理を呼びたい(例えばログやトランザクション開始など)。 また、そのメソッドの終了後にも何らかの後処理を呼びたい(トランザクション終了など)。

そのような、メソッドに対するフック処理を書いてください。 ライブラリを使用してメソッドのフックを実現した場合は ライブラリの名前を紹介してくれると助かります。

Posted feedbacks - Java

空気を読まんと投稿。 java.lang.reflectパッケージを使用します。 InvocationHandlerインタフェースのinvoke()メソッドで前処理、後処理を実装します。 Proxy.newProxyInstance() で受け取ったインスタンスのメソッドを実行したときに、 InvocationHandler.invoke()が動作します。

インタフェースに対してのみ適用可能であることをお忘れなく。

実行結果。

proxy = class test.hook.$Proxy0

method called. name = execute

ServiceImpl.execute() called.

method returned.

return value = Hello, world.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package test.hook;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Hook {

    public Hook() {
        InvocationHandler handler = new TestInvocationHandler(new ServiceImpl());
        Service service = (Service) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), new Class[]{Service.class}, handler);

        System.out.println("return value = " + service.execute());
    }

    public static void main(String[] args) {
        new Hook();
    }
}

/** サービスのインタフェース */
interface Service {

    public String execute();
}

/** サービスの実装クラス */
class ServiceImpl implements Service {

    public String execute() {
        System.out.println("ServiceImpl.execute() called.");
        return "Hello, world.";
    }
}

/** メソッドのフックに使用するハンドラ */
class TestInvocationHandler implements InvocationHandler {

    private Service service;

    public TestInvocationHandler(Service service) {
        this.service = service;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result;
        System.out.println("proxy = " + proxy.getClass().toString());
        System.out.println("method called. name = " + method.getName());  //前処理
        result = method.invoke(service, args);  //実際の処理
        System.out.println("method returned.");  //後処理
        return result;
    }
}

before/after は各 setter 経由で渡す。 なんか他の見てると、題意から外れてる気がしなくもない・・。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class HookSample {

    private Runnable before = new Runnable() { 
        public void run() { }
    };
    private Runnable after = new Runnable() {
        public void run() { }
    };

    public void call() {
        before.run();
        process();
        after.run();
    }

    protected void process() {
        System.out.println("main process...");
    }

    public void setBefore(Runnable r) {
        if (r != null) {
            before = r;
        }
    }

    public void setAfter(Runnable r) {
        if (r != null) {
            after = r;
        }
    }

}

題意を、呼び側も呼ばれ側も改造する事なく、開始終了のフックを行う事と解釈しました。

アスペクト指向を Java 上で実現する AspectJ を用いてメソッドの開始と終了にフックを行うサンプルです。下記の Sample クラスの printMessage メソッドに対するフックを行います。

public class Sample {
    public static void main(String[] args) {
        new Sample().printMessage("Hello, world.");
    }

    public void printMessage(String message) {
        System.out.println(message);
    }
}

言語は AspectJ と言った方が良いかも知れません。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public aspect Trace {

    pointcut message(): call(* Sample.printMessage(..));

    before(): message() {
        System.out.println("*** begin of printMessage() ***");
    }

    after(): message() {
        System.out.println("*** end of printMessage() ***");
    }
}

Index

Feed

Other

Link

Pathtraq

loading...