Spring Framework 入門記 AOPその13 AutoProxyCreator

今回は「5.7. Creating AOP proxies programmatically with the ProxyFactory」です.ProxyFactoryを使ってAOP Proxyを作る,と.ふむふむ.
な,なんですとぉー? 昨日どこにも書いてなーい! って文句を書いたProxyFactoryの使い方が,ちゃんと書いてあるではないですか! 与太話はいいから説明してくれよって書いたことが,ちゃんと説明してあるではないですか! ...心より恥じる.
うー,なんてこった.そういうことなら5.7じゃなくて5.6.3にしてくれよぉ.それなら気づいたのにぃ.しくしくしく.やっぱり少しは先読みしておかなきゃダメか.無念だ.
しょーがねー,軽く読んでスキップだ.AOP Proxyを作るには,ProxyFactoryを使って,addInterceptor(MethidInterceptor)addAdvisor(Advisor)とかでWeavingするAspectを指定して,最後にgetProxy()AOP Proxyを取り出せばよいわけね.オッケー,昨日調べたとおりだよ.もういいよ.バイバイ〜.


ということで,「5.8. Manipulating advised objects」に進みます.
ProxyFactoryで作成されたAOP Proxyは,Advicedというinterfaceimplementsしているとのこと.ふむふむ.このinterfaceのメソッドを使うことで,新たにAspect(Interceptor/Advisor)を追加したり削除したりできるそうです.ふーん.
あまり使う機会なさそうだなぁ.まぁ,必要に迫られたときにはそういうこともできるとだけ覚えておくことにして,スキップしちゃいましょう.心より恥じる.


本日はこれで終わり,というのもちゃんちゃらおかしいので,次は「5.9. Using the "autoproxy" facility」に進みます.
これまで,AOP ProxyはProxyFactoryBeanなどを使って明示的に作成してきたわけですが,実はもっとお手軽な方法があったようです.それが autoproxy というもので,コンテナがロードされたときに自動的にAOP Proxyを作成してくれるとのこと.いいねぇ.
なお,この仕掛けはBeanPostProcessorを使って構築されているらしいです.このinterface,「Chapter 3. Beans and the ApplicationContext」にチラッとでてきていたのですが,ろくな解説がなかったので気づかなかったことにしていたんですよね.心より恥じる.
どうやらこのBeanPostProcessorのメソッドは,コンテナがBeanのインスタンスを作るたびにコールバックされるので,そこでBeanをいろいろと細工できるということらしいです.そこでProxyFactoryに食わせてあげるってわけなのでしょう.なるほど.
それでその,autoproxyを使うには,大雑把に言って二つの方法があるとのこと.

むむ,メタデータ... あの.NETで使われている,あれのこと? JDK1.5で導入される,あれのこと? っていうよりむしろXDocletのあれのこと?
興味は尽きませんが,とりあえず順番に行きましょう.


まずはautoproxy creatorを使う方法です.autoproxy creatorの実装として,二つのクラスが用意されているようです.

  • BeanNameAutoProxyCreator
  • DefaultAdvisorAutoProxyCreator

まずはBeanNameAutoProxyCreatorについて.
これは名前のとおり,指定された名前(<bean>要素のid属性やname属性)のBeanに対してAspectをWeavingしてくれるautoproxy creatorです.ProxyFactoryBean同様,interceptorNamesというプロパティがあり,適用するAspect(Interceptor/Advisor)を指定することができます.なるほどなるほど.簡単に使えそう.
次にDefaultAdvisorAutoProxyCreatorですが,こちらはもっとeXtreamでpowerfulなautoproxy creatorとのことです.そのうえautomagicallyなんだって.automaticallyじゃなくて.ワクワク?
どういうことかというと,BeanにマッチするPointcutがあれば,それを片っ端から適用してくれるらしいです.そういえばSpringのPointcutは,ClassFilterMethodMatcherから構成されていました.なるほど,こういう使い方をするためだったわけですか.ProxyFactoryBeanを使っていたときは,ClassFilterなんて冗長な感じがしたものですが,これで納得出来ました.
なお,DefaultAdvisorProxyCreatorで適用できるのはAdvisorだけで,Interceptorではダメとのこと.InterceptorはAround AdviceですからPointcutを持っていないためでしょうね.そういう場合は,DefaultPointcutAdvisorなどを使ってPointcutと組み合わせてあげれば大丈夫なはず.
autoproxy creatorを使うと,BeanとAspectは,AspectJに近い関係になりますね.BeanはAspectの事はまったく知らなくて,Aspectの側が対象となるBeanをを知っているというわけなので.ということで,「Compontn配下のAspect」で書いたようなことが気になります.
その一つ,Aspectが適用される順番ですが,SpringではOrderedというinterfaceを用意しています.これは,「AOPその10 Introduction Advice」でまったく無意味な使い方をしたものなのですが,実はDefaultPointcutAdvisorなど,たいていのAdvisorはこれをimplementsしています.このinterfacegetOrder()というメソッドを持っていて,これでAdviceに適用する順序を持たせることができるということだと思われます.AspectJdeclare precedenceと同じ考え方ですね.うーん.
それからAspectがどのBeanに適用されるかについては,実行時のログで確認するしかなさそうです.無念だ.あ,もしかして世間で話題になっているSpring UI for Eclipseとか使うとそういうことも分かっちゃうのかなぁ? それならいいかも.インストールもしてないんだよな.心より恥じる.


さてさて,例によってつまらないものを作ってみましょう.役に立たなくてもいいもんね.どうせ日記だしぃ.
ということで,今回はDefaultAdvisorProxyCreatorを使ってみます.あらかじめ言い訳を済ませてあるので,遠慮なくDebugInterceptorを使いましょう.
まずはターゲットになるクラスを作成します.

package study;

public class Foo {
    public void one() {
        System.out.println("one");
    }
    public void two() {
        System.out.println("two");
    }
    public void three() {
        System.out.println("three");
    }
}

申し訳ないくらいに役立たずのメソッドが3つも.
次に,定義ファイルを用意します.

<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>

<bean id="debugAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="pattern"><value>study\..*\.[a-z]{3}</value></property>
    <property name="advice">
        <bean class="org.springframework.aop.interceptor.DebugInterceptor"/>
    </property>
</bean>

<bean id="foo" class="study.Foo">
</bean>

正規表現(もろパクリ)で指定したパターンにマッチすればDebugInterdeptorを適用してくれるはず.
それから,RegexpMethodPointcutAdvisorのパターンがクラスの完全限定名込みでマッチングを行う理由もわかりますね.複数のクラスにまたがって適用する場合に一気に記述できます.
ということで,Fooのメソッドを順次呼び出すようなクラスを作成して,実行!!!

 - Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory 
     defining beans [propertyPlaceholderConfigurer,customEditorConfigurer,autoProxyCreator,debugAdvisor,foo]; 
         Root of BeanFactory hierarchy]
 - Creating shared instance of singleton bean 'foo'
 - Candidate Advice [org.springframework.aop.support.RegexpMethodPointcutAdvisor@1bcdbf6] accepted for class [study.Foo]
 - Creating implicit proxy for bean 'foo' with 0 common interceptors and 1 specific interceptors
 - Bean 'context' instantiated via constructor 
     [public org.springframework.context.support.ClassPathXmlApplicationContext(java.lang.String[]) 
         throws org.springframework.beans.BeansException]
 Debug interceptor: count=1 invocation=[Invocation: method=[public void study.Foo.one()] 
     args=[Ljava.lang.Object;@6782a9] target is of class study.Foo]
 one
 Debug interceptor: next returned
 Debug interceptor: count=2 invocation=[Invocation: method=[public void study.Foo.two()] 
     args=[Ljava.lang.Object;@6782a9] target is of class study.Foo]
 two
 Debug interceptor: next returned
 three

となって,見事にone()およびtwo()にはDebugInterceptorが適用されました.一応FooRegexpMethodPointcutAdvisorが適用されたことがメッセージから伺えますが,Advisorのインスタンスが複数あると何が適用されたか分からなさそうです.無念だ.


個人的にはS2的にbean(S2ではcomponent)の下にaspectを定義するほうが明示的で好みなのですが,Springではautoproxy creatorを使うほうが使い勝手がよいのかもしれません.無念だ.
ということで,今日はここまで.次回は残りのメタデータを学習します.