\(^o^)/
10連勝です.また負けたキングスに並んでしまいました.直接対決では負け越している(1勝2敗)のにレイカーズが地区首位なんですね.なんにせよめでたい!
「MDA導入ガイド」11-12章
残りあと少しなので,頑張りましょう.
11. OMG標準とその他の技術
本章では,UMLを始めMOF,CWMといったOMGの標準技術について解説しています.
その中で注目はQVT(Query,Views,and Transformations).これは,MOFで定義されたモデル間の変換ルールを定義するというものだそうです.そういうものの標準化が始まっているというだけで,実際にどのようなものになるのかは触れられていなのですが,非常に興味深いです.
12. MDAの展望
MDAによるモデルからのコード生成を,高級言語(手続き型言語)から機械語へのコンパイルに例えて,MDAのパラダイムシフトは必然なのだとしています.将来は,現在機械語を意識する人がほとんどいないのと同様,コードさらにはPSMさえも意識されなくなるだろうとのことです.ふーむ.
開発プロセスでは,やはり分析の成果物としてPIMが作られるとのこと.ということは,いずれPSMが意識されなくなれば,設計工程がなくなるということ? 本当かなぁ? 本書を最後まで読んでも,この疑問は解消されませんでした.
ということで,一応読破しました.全体として,とても平易な記述で,これからMDAの学習を始める人には最適なのではないでしょうか.その分,すでに雑誌等でMDAの概要を理解している人が,より深くMDAを理解したいという場合にはもの足りないのではないかと思います.
お仕事スタイル
- こげ茶のシャツブルゾン(EZ BY ZEGNA)
- ベージュのニット(allegri)
- オフホワイトのコットンパンツ(HELMUT LANG)
- こげ茶のスエードのチャッカブーツ(Yanko)
自分には珍しい茶系のコーディネートです.ニットには肩のところにこげ茶が入っていて,ベルトもこげ茶のスエードのもの.ベージュのニットとオフホワイトのパンツだとパジャマっぽい感じなのを,肩・腰・足元のこげ茶で引き締めている... つもりなのですが,やっぱりパジャマかも.心より恥じる.
Spring Framework 入門記 AOPその5 Pointcutその他
今回は,Pointcutの仕上げとしてSpringが標準で用意してくれているPointcutの実装クラスについて学習します.
Static pointcut
NameMatchMethodPointcut
「AOP その2」で使用した,Joinpointのメソッド名でマッチングを行うPointcutです.Advisor
インタフェースを実装したNameMatchMethodPointcutAdvisor
も用意されています.
このPointcutは,mappedNames
という文字列の配列プロパティを持っています.このプロパティに設定する文字列には,ワイルドカードとして'*'
を含めることが出来ます.といっても,先頭または末尾のみ有効なようで,途中に書いても効果はなさそう.無念だ.そういう場合は後述の正規表現を使用するPointcutを使えということでしょうね.
matches(Method, Class)
メソッドは,Joinpointのメソッド名がmappedNames
プロパティのいずれかの文字列と一致(ワイルドカード含む場合はマッチ)すればtrue
を返します.
RegexpMethodPointcut
NameMatchMethodPointcut
と同様,Joinpointのメソッド名でマッチングを行うPointcutですが,正規表現を使うことができます.Advisor
インタフェースを実装したRegexpMethodPointcutAdvisor
も用意されています.
このPointcutは,pattern
という文字列のプロパティを持っていて,正規表現を設定することが出来ます.正規表現の実装には,Jakarta OROが使用されているようで,たとえJDK1.4で実行する場合でも,java.util.regexは使われないようです.無念だ.OROということでパフォーマンスが気になるところではありますが,このPointcutはStaticであるため,正規表現のマッチングが行われるのはAOP Proxy作成時だけです.あまり神経質になる必要はないかもしれません.
matches(Method, Class)
メソッドは,Joinpointのメソッド名が正規表現とマッチすればtrue
を返します.
RootClassFilter
これはPointcutそのものではなくて,ClassFilter
インタフェースの実装です.他のPointcutから利用されることを意図しているのでしょう.コンストラクタでClass
を設定することができます.matches(Class>
メソッドは,Joinpointのターゲットオブジェクトのクラスが,コンストラクタで渡されたClass
に適合する(Class#isAssignableFrom(Class)
がtrue
を返す)場合にtrue
を返します.
Dynamic pointcut
ControlFlowPointcut
AspectJのcflowのようなPointcutで,あるJoinpointが呼び出されている間の全てのJoinpointを採用するというPointcutです.コンストラクタでクラスとメソッド(省略可)を指定することが出来ます.
matches(Method, Class, Object[])
は,現在のスタックフレームを調べて,コンストラクタで指定されたクラス・メソッドが含まれている場合にtrue
を返します.でもでも,JDK1.3以前のための実装は少し怪しい感じ.メソッド名を省略した場合,クラス名だけでチェックを行うのですが,単純にString#indexOf(String)
を使っているので,例えばFoo
を指定するとFooBar
も引っかかってしまうような? JDK1.4以降を使えばいいんですけどね.
スタックフレームを取得してマッチングを行うことから,通常のPointcutよりもかなり遅いので気をつけろとのことです.JDK1.4で5倍くらい,JDK1.3だと10倍以上だとか.
その他
ComposablePointcut
複数のPointcutを組み合わせることの出来るPointcutです.組み合わせ方として,Pointcutの和を取るunion
,積を取るintersection
を使うことが出来ます.ただし,union
についてはClassFilter
およびMethodMatcher
を組み合わせることは出来ますが,Pointcut
を組み合わせることは出来ません.そのような場合は,後述のUnionPointcut
を使えとのことです.intersection
はPointcut
も組み合わせることが出来ます.
前回作成したDynamic Pointcutの修正版では,効率を考えてmatches(Method, Class)
メソッドとmatches(Method, Class, Object[])
の両方を実装しましたが,むしろNameMatchMethodPointcut
のようなStatic pointcutとをintersectionして使うほうがSpring流なのかもしれません.
UnionPointcut
コンストラクタで設定された二つのPointcutの和を取るPointcutです.
ComposablePointcut
ともども,Springにしては珍しくプロパティではなくコンストラクタで設定を行います.どちらかというと,定義ファイルに記述して使うよりも,他のPointcutの実装などからプログラマティックに使われることを想定しているのかもしれません.
おおむねRegexpMethodPointcut
があれば困らないような気がするわけですが,あって困るものでもないので機会があればありがたく使わせていただくことにします.
といったところでPointcutは終了にします.
... と思ったのですが,何もコードを書かないのはブログ^h^h^h日記としてどうよ? という気がしたので,とりあえず何かサンプルを作ります.おもしろそうなのはControlFlowPointcut
ですが,有用そうなのはComposablePointcut
だし... そんな場合は両方使ってしまいましょう.うん,その方が素敵(キラッ)!
まずPointcutを作ります.これは,プロパティに設定されたPointcutの配列全てのintersection(積)を取ってくれるようにします.というわけでこんな感じ.
package study; import org.aopalliance.aop.Advice; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.PointcutAdvisor; import org.springframework.aop.support.ComposablePointcut; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.Ordered; public class IntersectionPointcutAdvisor implements InitializingBean , Pointcut, PointcutAdvisor, Ordered { private Pointcut pointcuts; private Advice advice; private int order = Integer.MAX_VALUE; private ComposablePointcut composablePointcut = new ComposablePointcut(); //InitializingBean public void afterPropertiesSet() { if (pointcuts != null) { for (int i = 0; i < pointcuts.length; ++i) { composablePointcut.intersection(pointcuts[i]); } } } //Pointcut public ClassFilter getClassFilter() { return composablePointcut.getClassFilter(); } public MethodMatcher getMethodMatcher() { return composablePointcut.getMethodMatcher(); } //PointcutAdvisor public Pointcut getPointcut() { return composablePointcut; } public boolean isPerInstance() { throw new UnsupportedOperationException("perInstance property of Advisor is not yet supported in Spring"); } //getters and setters public Pointcut getPointcuts() { return pointcuts; } public void setPointcuts(Pointcut[] pointcuts) { this.pointcuts = pointcuts; } public Advice getAdvice() { return advice; } public void setAdvice(Advice advice) { this.advice = advice; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } }
プロパティに設定されたPointcut配列のintersectionを取るためにInitializingBean
をimplements
しています.
次に実験用のクラス.今回は,Foo
とBar
の二つのクラスを使います.
まずはFoo
.
package study; public class Foo { private Bar bar; public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } public void yoku() { System.out.println("よーく"); kangaeyo(); } public void kangaeyo() { System.out.println("考えよー,"); bar.okaneha(); } }
これは,Bar
への参照をプロパティで持っています.
次にBar
.
package study; public class Bar { public void okaneha() { System.out.println("お金は"); daijidayo(); } public void daijidayo() { System.out.println("大事だよー."); } }
そして定義ファイルですが,今回はBar
の方にAspectをWeavingします.そのAspectのPointcutにはもちろんIntersectionPointcutAdvisor
を使います.そのpointcuts
プロパティには,RegexpMethodPointcut
とControlFlowPointcut
を設定します.
こんな感じ.
<?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="foo" class="study.Foo"> <property name="bar"><ref bean="bar"/></property> </bean> <bean id="barTarget" class="study.Bar"> </bean> <bean id="bar" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"><ref bean="barTarget"/></property> <property name="interceptorNames"><value>advisor</value></property> </bean> <bean id="advisor" class="study.IntersectionPointcutAdvisor"> <property name="pointcuts"> <list> <bean class="org.springframework.aop.support.RegexpMethodPointcut"> <property name="pattern"><value>.*\.okaneha</value></property> </bean> <bean class="org.springframework.aop.support.ControlFlowPointcut"> <constructor-arg index="0"><value>study.Foo</value></constructor-arg> </bean> </list> </property> <property name="advice"><ref bean="interceptor"/></property> </bean> <bean id="interceptor" class="org.springframework.aop.interceptor.DebugInterceptor"> </bean> </beans>
RegexpMethodPointcut
のpattern
プロパティには".*\.okaneha"
を指定しています.実はこのPointcut,マッチングの際に使用するメソッド名は,そのクラスの完全限定名で修飾されていたんですね.ということで,任意のクラス(といっても今回の場合は意味がないのですが)のokaneha()
にマッチするように正規表現を指定しました.
ControlFlowPointcut
については,引数が1つのコンストラクタで初期化するようにしています.この場合の引数はClass
ですので,このPointcutはスタックフレーム中にstudy.Foo
が含まれていればマッチすることになります.
この2つのPointcutのIntersectionが,Bar
に適用される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.yoku(); Bar bar = (Bar) context.getBean("bar"); bar.okaneha(); ref.release(); } catch (Exception e) { e.printStackTrace(); } } }
そして実行!!!
よーく 考えよー, Debug interceptor: count=1 invocation=[Invocation: method=[public void study.Bar.okaneha()] args=[Ljava.lang.Object;@1556d12] target is of class study.Bar] お金は 大事だよー. Debug interceptor: next returned お金は 大事だよー.
このように,Foo
を経由して呼び出された場合のBar#okaneha()
にはAspectが適用されて前後にトレースが出力されています.しかし,Foo
を介さずにMain#main()
から呼び出された場合にはAspectが適用されていません.ということで,少なくともControlFlowPointcut
が効いていることが確認できました.RegexpMethodPointcut
の方は,今回の例ではあってもなくても同じ? 心より恥じる.
それにしても,やっぱりこういうものは動かしてみないと分からないことが多々ありますね.今回,実は2つの点ではまりました.無念だ.
一つ目は,すでに書いたようにRegexpMethodPointcut
のpattern
プロパティの指定の仕方です.てっきりメソッド名だけをパターンでマッチングするのだと思ってしまったんですよね.なぜって言われると困るのですが.ということなので,メソッド名に限らずクラス名の部分にも正規表現でフィルタリングすることが出来ます.Springの場合,PointcutもひとつのBeanであり,様々なターゲットに同じPointcutを適用できるので,これは重要なことなのかもしれません.
もうひとつはまってしまったことは,Aspectが適用されるのは,AOP Proxyを経由したメソッド呼び出しの場合だけということです.当然ですよね.でもはまっちゃいました.心より恥じる.今回テスト用に作ったFoo
とBar
は,意味もなく自分のクラス(インスタンス)のメソッドを呼び出しています.この場合には,AOP Proxyを経由しないため,Aspectは適用されないんですね.このことになかなか気づくことが出来ませんでした.これって,意外と痛い制約にならないのでしょうか? ちょっとドキドキです.
ということで,今度こそ本当にPointcut終了です.次回からはAdviceです.