続いて、S2によるAOPをどのようにして行えばよいかについて見ていきましょう。
AOPを行うツールには、AspectJやSpringなどがありますが、アスペクトをどのようにしてウィービングするかによって、大きく2つのタイプに分けることができるでしょう。1つは、AspectJのようにアスペクトを別のクラスとして作成しておいて、ウィービングを一括して行うタイプです。そしてもう1つは、アスペクトをインターセプタ(Interceptor)として作成しておき、DIコンテナでインスタンスを生成する際にこれをウィービングするタイプです。S2やSpringはこちらのタイプに分けることができます。
S2では、よく利用されると思われるアスペクトがあらかじめ既製のインターセプタとして用意されています。その一覧を表1に掲げます。S2におけるインターセプタを用いたアスペクトは、基本的に本質的関心事のメソッドを実行する前後に割り込ませて実行させるAroundアドバイスに相当します。これらのインターセプタの使用法はSeasarプロジェクトのドキュメント「S2AOPで用意されているInterceptor」(http://www.seasar.org/aop.html#AOPInterceptor)を参照してください。
独自にアスペクトを実装する場合も、org.aopalliance.intercept.MethodInterceptorインターフェイスを実装するか、すでにこのインターフェイスを実装済みの抽象クラスorg.seasar.framework.aop.interceptors.AbstractInterceptorを継承するかのどちらかで行います。この場合は、実装の方法によってBeforeアドバイスにしたり、Afterアドバイスにしたりすることは可能です。
クラス名 | 働き |
---|---|
TraceInterceptor | メソッドの実行をログに出力するトレース処理を行う |
ThrowsInterceptor | これを継承して例外発生時の処理を記述する |
TraceThrowsInterceptor | 例外の発生をトレースする |
MockInterceptor | オブジェクトをモックとして振る舞わせる(テスト用) |
DelegateInterceptor | ほかのクラスのメソッドに処理を委譲する |
PrototypeDelegateInterceptor | S2コンテナからインスタンスを取得して、そのインスタンスのメソッドに処理を委譲する |
SyncInterceptor | メソッド呼び出しを同期させる |
InterceptorChain | 複数のインターセプタをネストさせて実行する |
表1に掲げた既製のアスペクトの中から、ThrowsInterceptorを継承して例外処理を行うアスペクトを実装する方法を紹介します。サンプルとして用いるのは、平均値を求めるアプリケーションです。そのソースコードをリスト6、diconファイルをリスト7に示します。このとき、もし平均値を求める際に、計算の基になる引数がまったく設定されていなかったらメッセージを表示するアスペクトを実装してみましょう。
まず、きちんと引数を設定してこのアプリケーションを実行した結果を以下に示します。
実行結果 |
|
続いて引数を設定しないでこのアプリケーションを実行した結果を以下に示します。
実行結果 |
|
実行結果の最初の2行が、これから作成するアスペクトにより出力されたメッセージです。平均値を求めようにも引数が設定されていませんので、NullPointerExceptionが発生します。それをインターセプタで受け取って「getAverageメソッドで平均値を計算できませんでした」というメッセージを表示しているわけです。
この処理を行うインターセプタをリスト8のように作成しました。ThrowsInterceptorを継承して例外処理を行うアスペクトを作成する場合は、必ずhandleThrowableというメソッドを実装することになっています。この部分に例外が発生した場合の処理を記述します。
リスト8では、このメソッドの第1引数をThrowableにしていますので、すべての例外をこのメソッドで受け付けて処理することになりますが、この引数を別のExceptionクラスに変更すれば、その例外(とそのサブクラス)のみを受け付けるメソッドにすることができます。複数の種類の例外を受け付けるようにしたい場合は、それぞれの例外に応じたhandleThrowableメソッドを複数実装することで、それに対応できるようになっています。
diconファイルでアスペクトの指定をするには、<component>〜</component>間に<aspect>タグを記述します。pointcut属性にポイントとカットとなるメソッド名を記述します。そして<aspect>〜</aspect>間には、事前にインターセプタをコンポーネントとして記述したときの名称(name属性の値)か、<component>タグ自体を記述します。これによりコンポーネントにアスペクトが設定されます。なお、1つのコンポーネントに複数のアスペクトを設定する場合は<aspect>タグが複数記述されることになります。
リスト6 平均値を求めるアプリケーション [AverageImpl.java] |
package myfirst.aop; |
リスト7 diconファイル |
<?xml version="1.0" encoding="Shift_JIS"?> |
リスト8 ThrowsInterceptorを継承して作成したインターセプタ [noArgsInterceptor.java] |
package myfirst.aop; |
今度は、既製のものではない独自のアスペクトを作成してみましょう。作成するのは、引数の最大値を求めるアスペクトです。ここではAbstractInterceptorを継承して作成する例(リスト9)を紹介します。
リスト9の場合でも、MethodInterceptorインターフェイスを実装する場合でも、アスペクトとしての処理は必ずinvokeメソッドに実装します。そしてその内部では、ポイントカットとして指定されたメソッドをproceedメソッドで実行し、その結果を返す、というのが基本的な流れです。
proceedメソッドの前に何らかの処理を実行するものはBeforeアドバイスとなり、このメソッドの後で何らかの処理を実行するものはAfterアドバイスになります。proceedメソッドの前後どちらとも何らかの処理が行われるものはAroundアドバイスとなります。リスト9の場合は先にproceedメソッドが実行されていますので、これはAfterアドバイスということになります。
これをアスペクトとしてリスト7に追加したものがリスト10です。アスペクト自身の<component>タグと、それをアスペクトに指定する<aspect>タグを確認してみてください。
リスト9 AbstractInterceptorを継承して作成したアスペクト(引数の最大値を求める) [MaxValueIntercepter.java] |
package myfirst.aop; |
リスト10 diconファイルにリスト9のアスペクトを追加したもの [average.dicon] |
<components> |
今回はS2におけるDIとAOPについて見てきました。次回は、S2におけるデータベース関連の機能を見ていきます。
Copyright © ITmedia, Inc. All Rights Reserved.