AOP(Aspect Oriented Programming)を簡単にいうと元のソースコードに手を加えることなく、透明なサービスを提供する技術といえるのではないかと思います。透明というのは、使っている側からその存在が見えないという意味です。キーとなる概念には次のようなものがあります。
プログラム中に挿入されるコードを表します。Interceptorと呼ばれることもあります。
対象となるクラスとAdviceを結合するポイントを表します。AdviceはJoinpointから引数やメソッドの情報を取得することができます。
どこにJoinpointを設定するのかを定義します。
AdviceとPointcutを関連付けます。
S2AOPは、AOP Allianceに準拠しています。それでは、Seasarに最初からついているトレースのためのInterceptorを見てみましょう。
package org.seasar.framework.aop.interceptors;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class TraceInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
StringBuffer buf = new StringBuffer(100);
buf.append(invocation.getThis().getClass().getName());
buf.append("#");
buf.append(invocation.getMethod().getName());
buf.append("(");
Object[] args = invocation.getArguments();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; ++i) {
buf.append(args[i]);
buf.append(", ");
}
buf.setLength(buf.length() - 2);
}
buf.append(")");
System.out.println("BEGIN " + buf);
try {
Object ret = invocation.proceed();
buf.append(" : ");
buf.append(ret);
return ret;
} catch (Throwable t) {
buf.append(" Throwable:");
buf.append(t);
throw t;
} finally {
System.out.println("END " + buf);
}
}
}
Interceptorを実装するには、MethodInterceptorをimplementsしてinvokeメソッドを実装します。MethodInvocationのgetThis()、getMethod()、getArguments()で対象となるオブジェクト、メソッド、引数を取得できます。proceed()を呼び出すと実際のメソッドが呼び出され実行結果を取得することができます。
それでは、DateクラスにTraceInterceptorを適用します。対象となるメソッドはgetTime()とします。
Pointcut pointcut = new PointcutImpl(new String[]{"getTime"});
Aspect aspect = new AspectImpl(new TraceInterceptor(), pointcut);
AopProxy aopProxy = new AopProxy(Date.class, new Aspect[]{aspect});
Date proxy = (Date) aopProxy.create();
proxy.getTime();
PointcutImplのコンストラクタの引数で対象となるメソッド名を指定(複数可)します。AutoNumberImplのようにインターフェースを実装しているなら、new PointcutImpl(AutoNumberImpl.class)のようにクラスを指定することで、そのクラスが実装しているインターフェースのメソッドをすべて自動的に適用させることもできます。メソッド名には正規表現(JDK1.4のreqex)も使えます。
AopProxyのコンストラクタで、対象となるクラスとAspectの配列を指定します。その後、create()でAspectが適用されたProxyオブジェクトを取得できます。
例外処理をCrosscutting concernとして扱えるように、S2ではorg.seasar.framework.aop.interceptors.ThrowsInterceptorが用意されています。使い方は簡単で、ThrowsInterceptorを継承し、Object handleThrowable(Throwable, MethodInvocation)を実装するだけです。ThrowableにはThrowableのサブクラスを指定することができます。例えば、handleThrowable(IOException, MethodInvocation)のようにメソッド定義すると、発生した例外がIOExceptionもしくはIOExceptionのサブクラスの場合に、 呼び出されることになります。handleThrowable()はいくつでも定義することができます。
メソッド呼び出しをAspectを使って同期化できるように、S2ではorg.seasar.framework.aop.interceptors.SyncInterceptorが用意されています。ソースを変更することなく、メソッド呼び出しを同期化できます。