SE のための金融入門
- 作者: 小泉保彦
- 出版社/メーカー: 金融財政事情研究会
- 発売日: 2010/06
- メディア: 単行本
- 購入: 3人 クリック: 20回
- この商品を含むブログ (4件) を見る
ふと目についたので衝動買い。ちょうど銀行業務にも興味があったところなので、何となく斜め読みして気になったところを纏めてみた。銀行は証券会社と違って間接金融なのでとにかくリスクを取らないイメージがある。じゃあ実際どんな風に業務をしているんだろう?
銀行の収益が向かう方向
銀行の収益の大半は利鞘、つまり貸出や有価証券の運用利回りと預金との調達コストの差から生み出されている。従って銀行は常に貸出や運用利回りを高くすること、預金金利を低くすることを考えている。しかし金融自由化の影響で競争が激化し、利鞘収入によるビジネス(ストック・ビジネス)が難しくなってきている。そこでフロー型のビジネス、投資信託の販売などによる手数料ビジネスを強化している。近年銀行が証券業務や保険業務への参入を強く希望していたのもこのような背景に起因している。バンキングだけでなくトレーディング業務も出来るように体制を強化しているってことですね。
間接金融と直接金融
直接金融を担う証券会社などは、あくまで投資家と企業の仲介を行うだけで、その投資に対して責任を追わない。一方で銀行は間接金融を担い、投資家は安全に銀行に預金し、銀行が企業に貸出を行う。銀行は投資家の投資リスクを遮断しているとも言える。さらに銀行は投資家である預金者に対して元本を保証しているため、仮に企業が破綻したとしても預金の払い出しに応じないわけにはいかない。このようなリスクの非対称性から、銀行は貸出に対しては十分な審査を行うし、ポジションの管理も厳密になる。また、BIS 規制など自己資本比率に対する規制も厳しくなっている。
ヘッジ会計
これはこの書籍ではあまり触れられていなかったので、補足しておく。銀行業務では多くの場合、デリバティブはヘッジ目的で利用される。しかし時価会計適用が進む中、ヘッジに利用しているデリバティブは、それだけを抜き出して評価してしまうとヘッジ本来の趣旨とは合わなくなってしまう。例えば有価証券の価格変動リスクをヘッジするためには、多くの場合デリバティブである先物取引(時価評価適用対象)を行う*1が、先物でヘッジした場合、ヘッジ対象資産とヘッジ先物取引の損益計上時期がずれてしまう。ヘッジ関係を適切に示すためには、この計上時期を一致させる必要があるのでヘッジ会計が必要となる。具体的には繰延ヘッジと呼ばれる方法が用いられ、デリバティブ取引の評価損益をヘッジ対象の損益が認識されるまで繰り延べ、双方の損益を当期に認識しないようにする。すなわちデリバティブを時価評価しないということである。
信用リスクの把握
デリバティブの取引相手がデフォルトして予約した取引が実行出来なくなってしまった場合、市場で同じ取引を再構築する必要があり、これを再構築コストと呼ぶ。一般的に再構築コストは同取引の MTM となるが、実際には市況の変化により実際の約定条件は変化する可能性が高い。このため一般的には「MTM + 追加リスク」で再構築コストを算定する。これがカレント・エクスポージャ方式。追加リスクは基本的に想定元本に一定の掛け目を加えて計算し、この追加リスクをポテンシャル・エクスポージャと呼ぶ。
VaR 運用時の留意点
ヒストリカルデータはどれほど過去に遡及して採集するか。余りにも過去のデータまで使おうとすると、異なったマーケットのデータまで利用することになってしまい、サンプルデータとしての妥当性を担保出来ない。
異常値をどこまで VaR に考慮するか。特に株式はボラティリティが高いのでどこまで VaR に組み入れるか判断が難しい。
ポートフォリオの保有期間をどうするか。ポートフォリオをすぐに組み替えられるような銀行であれば保有期間は短く設定することができ、結果として価格変動リスクが少なくなって多額のポートフォリオを保有出来ることになる。しかし流動性の低い商品を多数扱っている銀行などではあまり短い保有期間を設定出来ない。ところが長期保有を前提とすると、価格変動リスクが大きくなって VaR が高まってしまい、資本が不足してしまうことになる。
BPV による判断
VaR より容易な方法として BPV が広く用いられているが、BPV が大きいとき = 保有ポートフォリオに対するリスクが大きいときに、そのポートフォリオの現状を放置しておくのか、それともポートフォリオを組み替えるのかは金利の見通し次第。一般に債券の感応度は残存期間に強く依存しているので、BPV が大きい場合には残存期間の圧縮が行われる。
銀行の収益管理体制
銀行では部門別・営業店別採算が行われている。この独立採算にあたっては本部と営業店の資金貸借金利(本支店レート)の設定に大きな意味があり、銀行独自の運用が行われている。
銀行収益の中核は利鞘収入であるが、営業店の中には預金中心の支店もあれば融資中心の支店も存在する。預金店は余剰資金を本部に貸し付けて運用し、融資店は不足資金を本部から借り入れて調達する。銀行は行内全体で余剰資金は本部に放出して融資店に回し、運用してもらう必要があり、そのために各営業店固有の預貸バランスを組み合わせて銀行全体として望ましい預貸バランスを確保している。このような運用のために営業店の独立採算制と本支店レートが採用されている。例えば、本支店レートを高くすれば営業店は貸出を増やすよりも預金を本部に付け替える方が成績が上がる。一方で本支店レートを低くすると、貸出を増やさないと収益が上がらなくなる。
資金決済システム
銀行では日々大量の振込依頼がなされ、その中には他行への振込依頼も多く含まれる。銀行同士の精算を個別に行っていてはとても業務が回らないので、銀行業界共通のインフラとして集中決済システムが存在する。集中決済システムでは、銀行同士の立替払いの受払尻を集計し、その結果を日銀に連絡して各行が日銀に持っている当座預金を使ってまとめて決済を行う。このような民間集中決済システムとして、全銀データ通信システムが存在する。
外国為替業務
銀行では業務の一貫として顧客からの依頼に基づく外貨の売買を行う。取引には直物為替と先物為替があり、先物予約は主に外貨債権/債務について、自国通貨に交換するまでの為替変動リスクを避けるために利用される。このような顧客との為替売買取引を対顧取引と呼ぶ。対顧取引によって銀行は外貨ポジションを持つことになり、為替リスクを負う。しかしこれまで書いた通り、銀行ではリスクを持つことは嫌われるため(O/N ポジションの持ち越しなどは一部ディーラーしか行えない?)、外国為替のインターバンク市場でヘッジを行うことになる。インターバンク市場は、為替ポジションを調整してスクエア化するための場となっている。
外国為替取引としては、対顧取引では直物(スポット)と先物(フォワード)が存在する。一方、インターバンク取引では直物・先物の他にスワップ取引が存在する。直物と先物は単独取引で、アウトライト取引とも言われる。対顧取引では先物予約が頻繁に行われているが、インターバンク市場では先物予約は殆ど扱われておらず、スワップ取引が中心となっている。これは、銀行にとって先物のアウトライト取引は投機的性格が強い上、インターバンク市場でなかなか出会いがつかないという事情による。
対顧取引のポジションをスクエア化する際の例を以下に示す。
- 企業から輸出代金のドル先物の売り予約が入る。
- ドル先物買いポジションをスクエア化するため、インターバンク市場で先売/直買のスワップ取引を実施する。
- ドル直物買いポジションをスクエア化するため、インターバンク市場で直売取引を実施する。
こんな感じかな。少し銀行業務のイメージが掴めた気がします。
Apache と Tomcat
Web アプリケーションに詳しくないので余り気にしていなかったのですが、良く考えると Apache や Tomcat って何だろう?と思った週末。
Google で調べてみると同じ疑問を持っている人がいました。回答を見ると、
- Apache HTTP Server:Web サーバ。アクセス制限やバーチャルホストを利用した運用などが可能。静的なコンテンツの表示を行える。
- Tomcat:サーブレットコンテナ。Java による動的なページの生成が可能。Web サーバの機能も備えているが, Apache に比べると貧弱なので通常は Apache と連携して利用する。
といった感じでしょうか。
続いて Wikipedia で調べてみると、Web サーバの記事はまぁ普通ですね。クライアントにより指示された URL により, Web サーバ内に存在するコンテンツを HTTP に則って返すものです。一方サーブレットコンテナは、今では余り厳密な意味では使われていないようですが, Java を用いて Web コンテンツを動的に生成するサーバー上で動作するプログラム(サーブレット)の実行環境のことです。一般にサーブレットはサーブレットアプリケーションとサーブレットコンテナが協調して動作しており, サーブレットコンテナはセッションやライフサイクルの管理を行います。Tomcat や Jetty, WebLogic, WebSphere, JBoss, Glassfish などは HTTP サーバとしての機能も備えているので, Web コンテナと呼ばれることもあるようです。
サーブレットと同様の技術としては CGI や mod_php などがあります。サーブレットはリクエストの度に新しいプロセスを生成する CGI に比べると, メモリに常駐してスレッドを立てて動作するので軽量・高速です。mod_php は Apache HTTP Server 上で PHP のプロセスを動かすモジュールです。
Web コンテナには主に以下の 3 つの種類があり, 大抵のコンテナはこれらを切り替えて使うことが出来ます。
- スタンドアローン型: サーブレットコンテナ自身が Web サーバを内蔵しているタイプ。
- 内部プロセス型: Web サーバのプロセスの一部としてコンテナが動作するタイプ。Web サーバとリソースを共有するため, 高速。但しスケーラビリティに乏しい。
- 外部プロセス型: Web サーバのプラグインと, 外部で動作するコンテナが連携するタイプ。内部プロセス型よりレスポンスは劣るが, スケーラビリティが高い。
取り敢えずこんな感じかな。後々, Apache で出来ることや Tomcat の仕組みなんかを知りたいですね。
ココに良く纏まった資料があるので参考になりました。
浮動小数点の計算誤差
唐突に浮動小数点の計算誤差って一体何なんだろう、ということをネットで調べてみましたメモ。勿論コンピュータに詳しい人なら皆知っているであろうことなんですが、僕はさっぱり知らないので調べてみました。そういえば興味本位で受けた基本情報処理の試験にもこんなトピックがあったような気もします(テキスト斜め読みしただけ・・・)。次回は BigDecimal について調べるつもりです。
Wikipedia によると、計算誤差には以下の種類があるそうです。
- 丸め誤差:どこかの桁で端数処理をした場合に発生する誤差
- 打ち切り誤差:計算を続ければ精度が良くなるにも関わらず、計算を途中で打ち切った場合に発生する誤差
- 情報落ち:絶対値の大きな数と絶対値の小さな数を加減算したとき、絶対値の小さな数が無視されてしまうこと
- 桁落ち:値がほぼ等しく丸め誤差を持つ数値同士を減算した結果、有効数字が減少すること
浮動小数点について
さて、まず浮動小数点って何なのか、から調べてみます。
浮動小数点は指数形式の数値ことで、例えば「1230」は「1.23E+03」と表現されます。これは「1.23*10^3」という意味。「1230」という数字の見た目の小数点位置と内部表現の小数点位置が異なるため、「浮動小数点」と呼ばれるそうです(つまり、内部表現によって小数点位置が動く)。反対に固定小数点は位置が動きません。固定小数点の方が分かり易いのに浮動小数点を使う理由は、広い範囲の数値を少ない桁数で表現出来るから。コンピュータは桁数が少ない方が計算が速いから、浮動小数点を使う方が有利なんですね。
浮動小数点は、基本的に「符号」「仮数」「基数」「指数」によって表現されます。例えば「-1.23*10^3」なら、「-」は符号、「1.23」は仮数、「10」は基数、「3」は指数です。「(符号)仮数×(基数の指数乗)」が浮動小数点の表現方法です。
浮動小数点にも色々な規格があるそうですが、Excel や Java など、メジャーどころではIEEE754という規格が使われているそうで、この規格では基数は「2」とします。つまり、数値を「(符号)仮数×(2の指数乗)」として表現します。データ形式には単精度と倍精度、更にそれぞれの桁数を拡張した拡張精度が存在します。
Java だと short が単精度浮動小数点数、double が倍精度浮動小数点数になりますね!単精度と倍精度は表現出来る範囲が違うだけで、仕組みは全く同じらしいです。単精度で表現出来る仮数は10進数で7桁まで、倍精度だと15桁までになるとのこと。
では、例えば「2.5」を倍精度浮動小数点として表現してみます。「2.5」を2進数に変換すると「10.1」になります。これは「10.1*2^0」です。IEEE754では仮数部は「1以上2未満」に正規化する必要があるので、正規化して「1.01*2^1」にします(仮数部を2で割って、指数部に2を掛けています)。次に、指数部にバイアス値1023を足します。IEEE754では、単精度の場合は127、倍精度の場合は1023がバイアス値で、これはマイナスの値を取りうる指数部をプラスの値に補正するためのものらしいです。
「1+1023=1024」を2進数に変換すると、これは2の10乗なので「10000000000」になります。仮数部は、整数部分1を省いた残りを格納します。整数部を省くのは、IEEE754では仮数部は常に1以上2未満に正規化されるため、敢えて記憶する必要が無いからです(復元するときは整数部を補う必要があるはず)。整数部を格納しないようにすると使えるビットが1つ増えるため、省略することになっているそうです。なので、小数部「01」を、右側はゼロで埋めて52ビット分を仮数部とします。これで浮動小数点が何なのかちょっと分かりました!
丸め誤差について
丸め誤差は流石の僕でも知っていますが、コンピュータの演算でも丸め誤差が発生するのは有名です。でも、何で浮動小数点の計算で丸め誤差が発生するんでしょう?
例えば「0.1」は2進数では表現出来ません。「0.0001100110011...」と続いていくだけです。こうなると、仮数部は決められた52ビットには入りきらないのでどうしても丸めるしかありません。IEEE754には5種類の丸めアルゴリズムがありますが、通常は「最近接丸め」という丸めが行われます。この丸めによって、実際の「0.1」よりは少し大きな数になってしまいます(数値によっては少し小さくなります)。
10進数から2進数への正確な変換は出来ない数字を10進数に戻すことを考えます。2進数から10進数への変換は正確に出来るので(0と1で表現された値を2のX乗して足すだけだから)、丸められた結果が正確に得られます。こうして誤差が生まれます。
情報落ちについて
例えば、仮数部に8ビット分の情報しか保持出来ない浮動小数点演算で「1.1110000*2^50 + 1.1110000*2^30」を計算するとします。単純に足すことは出来ますが、結果を浮動小数点として表現する際は左項に比して非常に小さな値である右項は捨てられてしまいます。これが情報落ちです。「1.1110000*2^10 + 1.1110000*2^(-10)」ならば、計算結果「11110000000.0000000001111」の最初の8桁までしか情報として持っておくことができず、捨てられてしまうことになります。
桁落ちについて
例えば、「1.23456789*10^2 - 1.23456780*10^2」のような計算を行なうと、計算結果は「9*10^-6」となり、有効数字の桁数は9桁から一気に1桁に減少してしまいます。これが桁落ちです。
浮動小数点では内部的には常に有効数字の桁数を一定として扱っているため、上位の桁がゼロになると、正規化によってそれを詰め、以下の桁にゼロが強制的に挿入されるので、下位の桁が信頼できないものになります。
Lucy を読む (1)
全然勉強せずに遊び呆けてます。唐突に T2-framework の Lucy に興味を持ったので読 Lucy してみます。超気まぐれ。
何で興味を持ったかというと、先日 Seasar のソースコードを読んだので別の DI コンテナのソースコード読んでみたかったというのと、T2 自体がイマドキのライブラリなので、これを読んだらイマドキの Java ソースコードを勉強出来ると思ったからです。あ、web アプリケーションは作ったことが無いので T2 についてはサッパリ分かりません、ということで華麗にスルーします。ごめんなさい。
さて、Lucy は軽量な DI コンテナという点を売りにしているそうです。Google Guice と比べてみたい気もしますが、取り敢えずサンプルを読んで大体使用方法は分かったので、気が向くままに読んでみようと思います。取り敢えず Lucy をインポートして軽く読んだ中で「面白いなー」と感じた部分をメモ。
LucyImpl#assertComponentAnnotation
アノテーションを使用してコンポーネントをインジェクションする場合, @Component アノテーションが付与されているか否かを調べるアサーション処理です。Lucy は(多分)インジェクションする Bean に対して @Component を付けることは普通は無くて、@SingletonScope や @PrototypeScope のように Bean のスコープを定義するアノテーションを付与します。ここで面白いのは, 実は @SingletonScope や @PrototypeScope が @Component を付与されたクラスであるという点です。
assertComponentAnnotation では AnnotationUtil#hasAnnotationType を使って @Component アノテーションの有無を検査していますが、これは以下のようなソースコードになっています。
public static boolean hasAnnotationType(Class<? extends Annotation> target, Annotation[] annotations) { for (Annotation a : annotations) { if (target == a.annotationType()) { return true; } } for (Annotation a : annotations) { Annotation[] metas = a.annotationType().getAnnotations(); if (metas == null || isTopLevelAnnotations(metas)) { continue; } boolean b = hasAnnotationType(target, metas); if (b) { return true; } } return false; }
つまり、検査対象のアノテーションに付与されたメタアノテーションまで調査しているのですね。これによって、Bean に対して @Component と @SingletonScope のように2つのアノテーションを付与する必要が無くなっています。面白いなー、コレ。これがイマドキなのか・・・。
isTopLevelAnnotations は Java 標準のアノテーションか否かの検査ですが、この検査を行ってループを脱出しているのは処理高速化のためだと思われます。
Lucy のソースコードは全体的に綺麗で読み易いですね!でも↑の " boolean b " って部分はちょっとキライです(笑)。
今後の予定
またまた休んでしまいました。やれやれ。今後の計画を少々修正しようと思います。
まず、英語の勉強は暫く休止します。必要だということは十分分かっているつもりですが、どうしてもやる気が起きないので当面は延期することにしました。しばらくはコンピュータ関連の勉強を中心に進めようと思います。次に、『コンピュータの構成と設計』を勉強する予定でしたが、「どうせなら資格も取得しちゃおう!」と思ったので、代わりに応用情報技術者試験を受けることにしました。
というわけで、応用情報技術者試験の勉強と並行して、読みたかった『ゲームプログラマになるために覚えておきたい技術』を読もうと思ったのですが・・・僕、Javaしか知らない・・・。ので、まずは C++ の入門書を読んでみることにしました。
次に読む本のリスト
- 作者: 大滝みや子,岡嶋裕史
- 出版社/メーカー: 技術評論社
- 発売日: 2008/12/06
- メディア: 単行本(ソフトカバー)
- 購入: 2人 クリック: 7回
- この商品を含むブログ (11件) を見る
- 作者: 柴田望洋
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/09/16
- メディア: 単行本
- 購入: 3人 クリック: 70回
- この商品を含むブログ (6件) を見る
- 作者: 平山尚
- 出版社/メーカー: 秀和システム
- 発売日: 2008/11/14
- メディア: 単行本
- 購入: 112人 クリック: 3,473回
- この商品を含むブログ (193件) を見る
当然 C 言語も見たことが無いので、いきなり C++ を勉強しても駄目かなぁと思いつつ。でも今時 C なんて化石を勉強するのもなぁ・・・と思って、『明解 C++』にしてみました。店頭で見てないでいきなり買ったので、届くまで内容不明ですが。
■
[読Seasar] アスペクトの織り込み
以前の記事で、ComponentDeployer の初期化時に、デプロイ対象とするクラスにアスペクトを織り込むことが分かっています。AopProxyUtil#getConcreteClass(ComponentDef componentDef) で、アスペクトを織り込んだクラスを生成するのでした。ソースコードを再掲します。
/** * 完全なクラスを返します。 * * @param componentDef * @return 完全なクラス */ public static Class getConcreteClass(final ComponentDef componentDef) { if (componentDef.getAspectDefSize() == 0 && componentDef.getInterTypeDefSize() == 0) { return componentDef.getComponentClass(); } final Map parameters = new HashMap(); parameters.put(ContainerConstants.COMPONENT_DEF_NAME, componentDef); AopProxy proxy = new AopProxy(componentDef.getComponentClass(), getAspects(componentDef), getInterTypes(componentDef), parameters); return proxy.getEnhancedClass(); }
/** * {@link AopProxy}を作成します。 * * @param targetClass * @param aspects * @param interTypes * @param parameters */ public AopProxy(final Class targetClass, final Aspect[] aspects, final InterType[] interTypes, final Map parameters) { this.targetClass = targetClass; defaultPointcut = new PointcutImpl(targetClass); weaver = new AspectWeaver(targetClass, parameters); setupAspects(aspects); weaver.setInterTypes(interTypes); enhancedClass = weaver.generateClass(); }
defaultPointcut は、対象となるクラスが実装している全インターフェースのメソッドが対象となるポイントカット。AspectWeaver は後で使っているので一旦飛ばし、まず setupAspects を見てみる。
private void setupAspects(Aspect[] aspects) { if (aspects == null || aspects.length == 0) { return; } for (int i = 0; i < aspects.length; ++i) { Aspect aspect = aspects[i]; if (aspect.getPointcut() == null) { aspect.setPointcut(defaultPointcut); } } Method[] methods = targetClass.getMethods(); for (int i = 0; i < methods.length; ++i) { Method method = methods[i]; if (MethodUtil.isBridgeMethod(method) || MethodUtil.isSyntheticMethod(method)) { continue; } List interceptorList = new ArrayList(); for (int j = 0; j < aspects.length; ++j) { Aspect aspect = aspects[j]; if (aspect.getPointcut().isApplied(method)) { interceptorList.add(aspect.getMethodInterceptor()); } } if (interceptorList.size() == 0) { continue; } weaver.setInterceptors(method, (MethodInterceptor[]) interceptorList .toArray(new MethodInterceptor[interceptorList .size()])); } }
少々省略したけど、「うわぁ・・・」って感じ。分かりづらい・・・。
まず、アスペクトに対してポイントカットが無ければ defaultPointcut をセットしている。次に対象クラスのメソッドに対してインターセプタを仕込んでいる。この際、ブリッジメソッドか合成メソッドはこの処理を飛ばしている。記憶が曖昧だけど、確かどちらもコンパイラが自動生成するメソッドなので、無駄にインターセプタが仕込まれるのを省略してるんだろう。
/** * {@link MethodInterceptor}を設定します。 */ public void setInterceptors(final Method method, final MethodInterceptor[] interceptors) { final String methodInvocationClassName = getMethodInvocationClassName(method); final MethodInvocationClassGenerator methodInvocationGenerator = new MethodInvocationClassGenerator( classPool, methodInvocationClassName, enhancedClassName); final String invokeSuperMethodName = createInvokeSuperMethod(method); methodInvocationGenerator.createProceedMethod(method, invokeSuperMethodName); enhancedClassGenerator.createTargetMethod(method, methodInvocationClassName); final Class methodInvocationClass = methodInvocationGenerator .toClass(ClassLoaderUtil.getClassLoader(targetClass)); setStaticField(methodInvocationClass, "method", method); setStaticField(methodInvocationClass, "interceptors", interceptors); setStaticField(methodInvocationClass, "parameters", parameters); methodInvocationClassList.add(methodInvocationClass); }
う、ここから重い・・・。MethodInvocationClass って何だ?
/** * <code>proceed</code>メソッドのソースを作成します。 * * @param targetMethod * @param enhancedClassName * @param invokeSuperMethodName * @return <code>proceed</code>メソッドのソース */ public static String createProceedMethodSource(final Method targetMethod, final String enhancedClassName, final String invokeSuperMethodName) { final StringBuffer buf = new StringBuffer(1000); buf.append("{"); buf.append("if (interceptorsIndex < interceptors.length) {"); buf.append("return interceptors[interceptorsIndex++].invoke(this);"); buf.append("}"); buf.append(createReturnStatement(targetMethod, enhancedClassName, invokeSuperMethodName)); buf.append("}"); return new String(buf); }
と思ったら、メソッドをコールする際に全てのインターセプタを invoke するためのクラスを生成するものだった。で、この methodInvocationClass を使用してエンハンスするクラスのメソッドを生成している。このとき生成されるメソッドの本体は EnhancedClassGenerator#createTargetMethodSource で、methodInvocationClass を new して proceed() するもの。
最後に AspectWeaver#generateClass() で、エンハンスされたクラスを生成。
/** * クラスを生成します。 * * @return 生成されたクラス */ public Class generateClass() { if (enhancedClass == null) { enhancedClass = enhancedClassGenerator.toClass(ClassLoaderUtil .getClassLoader(targetClass)); for (int i = 0; i < methodInvocationClassList.size(); ++i) { final Class methodInvocationClass = (Class) methodInvocationClassList .get(i); setStaticField(methodInvocationClass, "targetClass", targetClass); } } return enhancedClass; }
恐ろしいほど意図が分からない・・・。methodInvocationClass の targetClass プロパティに対して対象クラスを設定している。何でこのタイミングで設定しているのかは謎。まぁ取り敢えずここでエンハンスされたクラスが完成するわけですね。最終的にはここで出来た enhancedClass を newInstance() する。
上記の処理をまとめると、次のようになる。
対象クラスのポイントカットとなるメソッドに対して、インターセプタをセットしていて、1つのメソッドに対するインターセプタは MethodInvocation として纏められる。MethodInvocation は proceed() によって各インターセプタが invoke されるようになっていて、対象メソッドが実行されると、MethodInvocation#proceed() が実行されるような enhancedClass が生成される。
つまり、アスペクトが織り込まれたメソッドの呼び出しは次のように実現される。
- MethodInvocation#proceed()
- Interceptor1#invoke(methodInvocation)
- Interceptor2#invoke(methodInvocation)
- Interceptor3#invoke(methodInvocation)
- ...
- 対象メソッド本体の実行
こんな感じ。うーん、疲れました。次は何を読もうかなぁ・・・。トランザクション制御の辺りは読んでみたいかも。
アスペクトの読み込み
S2AOP は S2Container とセットになった機能になっています。アスペクト定義は ComponentDef に含まれているはずなので、まずはどのように ComponentDef に登録されるのか見てみましょう。以前の記事を踏まえるとアスペクトの読み込みを行っているのは AspectTagHandler です。
public void start(TagHandlerContext context, Attributes attributes) { AspectDef aspectDef = null; String pointcutStr = attributes.getValue("pointcut"); if (pointcutStr != null) { String[] methodNames = StringUtil.split(pointcutStr, ", "); aspectDef = createAspectDef(createPointcut(methodNames)); } else { aspectDef = createAspectDef(); } context.push(aspectDef); }
ちゃんと createAspectDef や createPointcut が protected メソッドになっているのは好印象。拡張用意な設計です。
pointcut タグがあればポイントカットを作って AspectDef を生成しています。Pointcut はアスペクトをどこに適用するか、の定義。メソッド名やクラスを指定可能。正規表現も使えます。AspectDef はポイントカットに織り込まれる処理を記述した MethodInterceptor と、その対象となる Pointcut を保持する Bean。
end タグを処理する際、ComponentDef は AspectDef を登録・取得出来ることを示す AspectDefAware を実装しているので、ComponentDef に生成した Aspect を登録しています。
簡単ですね。では次は AspectDef をどのようにコンポーネントに織り込んでいるか見てみましょう。