Spring Framework 入門記 AOPその1 ProxyFactoryBean

さぁ,いよいよ「Chapter 5. Spring AOP: Aspect Oriented Programming with Spring」に突入です.今時のIoCコンテナの目玉とも言える代物ですよね.AOPAspectJで軽く遊んだことがあるだけで,この手のダイナミック(ランタイム)なAOPは初めてなので,とても楽しみです.では始めましょう.
... いきなりつまずいてしまいました. (;_;)
油断していましたが,今回参照しているドキュメントって,「Spring Reference Documentation」というタイトルなんですよね.チュートリアルではないのです.たとえ内容がとてもリファレンスには見えなくても,チュートリアルではないのです.自分としては,概要から少しずつ話を詳細化してくれるような解説を期待したいのですが,本章は逆にボトムアップ的に話を積み上げていくんですね.おかげで,始めの方をちょっと読んだだけでは何もできるようになりません.無念だ.
ま,気を取り直していきましょう.ドキュメントを始めからちゃんと読んでいくのがつらくなったら斜め読みです.詳細が理解できなくてもいいのです.なんとなく,全体像がつかめてきたら,改めて最初からじっくり読めばいいのです.
ということで,斜めにばく進しました.おぉ,「5.5. Using the ProxyFactoryBean to create AOP proxies」を発見! こういうのを最初に持ってきてくれよ.
AOPの基本要素といえば,Joinpoint,Pointcut,Advice,Introduction,そしてAspectといったところになると思うのですが,Springの場合,というよりダイナミックなAOPの場合,かな? ともかくそういう場合にはもう一つ,AOP proxyなるものが登場するようです.これは,AOPフレームワーク(今回の場合はSpring)が生成する,Adviceを組み込まれたオブジェクトとのことです.SpringのAOPは,AspectJのようにコンパイル時にAspectをWeavingするわけではないので,実行時に同等なことをしなければならないのですが,その手段として使われるのがProxyであり,そしてそのProxyを生成してくれるのがProxyFactoryBeanらしいです.むむ,この名前,見覚えがありますね.おぉ,やはり! こいつはおなじみのFactoryBeanではありませんか! あの,自身がFactoryであるところのBeanです.なるほど,これで見えてきました.JavaBeansマンセーなSpringは,ここでもBeanを駆使するのですね.大雑把に言ってしまうと,ProxyFactoryBeanは,BeanにAspectをWeavingしたBeanを返してくれるBeanです.今何回Beanって言った?
さぁ,ぐだぐだ能書きたれるよりも実践です.っていうか,Springのドキュメントは能書きが多いと思うぞ.いえ,そんなことはありません,ごめんなさい,とても感謝しています,本当です.
ということで,ともかく何か作りましょう.初めてのAOPといえばロギングです.そうに決まっています.今決めました.ロギングというよりトレースですね.メソッドが呼び出される前と後にメッセージを出すという,あれです.あれをやってみましょう.これはハローワールドと同じく,約束事なのです.
どうやらSpringでは,DebugInterceptorというそのまんまなことをやってくれるAdviceが用意されているようなので,これを使うことにします.なお,SpringではaroundなAdviceはInterceptorと呼ぶようです.これは,AOP Alianceに準拠するためだとか.斜め読みでぶっ飛ばしたところなので間違っているかもしれませんが.
ともかくですね,まずはAspectを適用する対象となるクラスを作ります.

package study;

public class Foo {
    public void run() {
        System.out.println("Hello, AOP");
    }
}

次に,これとDebugInterceptorProxyFactoryBeanを定義したXMLファイルを作成します.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="fooTarget" class="study.Foo">
    </bean>

    <bean id="interceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
    </bean>

    <bean id="foo" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target"><ref bean="fooTarget"/></property>
        <property name="interceptorNames"><value>interceptor</value></property>
    </bean>
</beans>

ここでの注目は,Fooインスタンスに付けた名前が"fooTarget"になっていることです.この名前は,これまでのようにアプリの方で使うためのものではありません.アプリがFooのインスタンスのつもりでコンテナから取り出すのは,ProxyFactoryBeanが生成するオブジェクトです.そのため,こいつの名前が"foo"になっています.このProxyFactoryBeanの定義では,Aspectを適用する対象として"fooTarget"を指定しています.そして適用するAdvice(Interceptor)としてDebugInterceptorを指定しています.Pointcutは? いいんですよ,そういう細かいことは後回しでいいんです.いやその,まだ未調査です,許してください.
とうことで(何が?),実行するためのクラスを用意します.

package study;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.context.ApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;

public class Main {
    public static void main(String[] args) {
        try {
            BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance();
            BeanFactoryReference ref = locator.useBeanFactory("context");

            ApplicationContext context = (ApplicationContext) ref.getFactory();
            Foo foo = (Foo) context.getBean("foo");
            foo.run();

            ref.release();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ここではBeanFactoryLocatorを使っているので,「Contextその6 邪悪なSingleton」で作成した"beanRefFactory.xml"が必要です.
これを実行すると...

Debug interceptor: count=1 invocation=[Invocation: method=[public void study.Foo.run()] 
        args=[Ljava.lang.Object;@c0f1ec] target is of class study.Foo]
Hello, AOP
Debug interceptor: next returned

と出力されました.メッセージが少々くどい気がしますが,ちゃんとDebugInterceptorが働いたことが分かります.
よかったぁ,どうにかベースラインができました.一瞬連載^h^h日記打ち切りかと思っちゃいましたよ,全く.よかったよかった.この後はどこから手を付けようかなぁ.Adviceを作るところから始めようかな.いいや,明日考えよう.from 「Gone with the Wind