GuiceでSeasar2のinterceptorを使ってみた

GuiceAOP Alliance APIを実装したinterceptorをサポートしています。interceptorを使うサンプルがid:arumaniさんのところの、"Google Guice(その3) AOP (http://d.hatena.ne.jp/arumani/20070312/1173674880)"で紹介されているように、Guiceではorg.aopalliance.intercept.MethodInterceptorインタフェースを実装したクラスを簡単にinterceptingできるようになっています。GuiceがSpringのinterceptorを利用できるというのもこの機能があるからです。AOP Alliance APIを実装しているといえば、Seasar2のinterceptorもそうなっています。GuiceSeasar2のTraceInterceptorを試してみました。

今回はトレースしたいメソッドをアノテーションで指定する方法を試してみましたので、まずはアノテーションを作りました。

package guicetest;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MethodTracer {}

次に、http://d.hatena.ne.jp/yokolet/20070313#1173843088で試してみたMainクラスにimport文を二つ、configureメソッド内にbindInterceprorメソッドを追加してinterceptorを設定しました。

package guicetest;

import static com.google.inject.matcher.Matchers.*;             // added
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Guice;

import org.seasar.framework.aop.interceptors.TraceInterceptor;  // added

public class Main {

    public Main() {
        Injector injector = Guice.createInjector(new GreetingModule());
        GreetingController controller = new GreetingController();
        injector.injectMembers(controller);
        controller.execute();
    }

    public static void main(String[] args) {
        new Main();
    }
    
    private class GreetingModule extends AbstractModule {
        protected void configure() {
            bind(Greeting.class).to(GreetingImpl.class);
            bind(GreetingClient.class).to(GreetingClientImpl.class);
            bindInterceptor(any(),                                      // added
                    annotatedWith(MethodTracer.class),
                    new TraceInterceptor());
        }
    }
    
}

以上で@MethodTracerアノテーションを使ってSeasar2のTraceInterceptorを指定できるようになりましたので、GreetingImplとGreetingClientImplに@MethodTracerを追加しました。

package guicetest;
...
...
public class GreetingClientImpl implements GreetingClient {
    ...
    ...
    @MethodTracer
    public void execute() {
        System.out.println(greeting.greet());
    }
    ...
    ...
}
package guicetest;

public class GreetingImpl implements Greeting {
    ....

    @MethodTracer
    public String greet() {
        return "Hello World!";
    }
}

さらに、Seasar2付属のlog4j.propertiesを用意して、必要なJarアーカイブを設定します。今回使ったのはguice-1.0.jar, aopalliance.jar, s2-framework-2.4.11.jar, commons-logging-1.1.jar, log4j-1.2.13.jarの5つです。これで実行できるようになったので、Mainクラスを動かしてみました。すると、以下のように表示されてTraceInterceptorをinterceptingできたことがわかります。

DEBUG 2007-03-17 17:00:12,200 [main] BEGIN guicetest.GreetingClientImpl#execute()
DEBUG 2007-03-17 17:00:12,203 [main] BEGIN guicetest.GreetingImpl#greet()
DEBUG 2007-03-17 17:00:12,209 [main] END guicetest.GreetingImpl#greet() : Hello World!
Hello World!
DEBUG 2007-03-17 17:00:12,209 [main] END guicetest.GreetingClientImpl#execute() : null

ここで紹介したアノテーションを利用する方法は、楽にメソッドを指定できるメリットがある半面、java.util.ArrayListjava.util.Dateクラスなど自分が作っていないクラスのメソッドを指定するのは難しいというデメリットもあります。java.utl.ArrayListのメソッドに適用する場合は、bindInterceptor()メソッドの引数でなんとかすることになるはずです。annotatedWith()メソッドの代わりに、com.google.inject.matcher.Matchersクラスが提供する、only(Object o)やsubclassesOf(Class superclass) メソッドを使えばできるのではと思います。