Seasar DI Container with AOP

S2AOPの概要

AOPとはAspect Oriented Programmingの略で、 複数のクラスに分散するロジックをモジュールとして抽出し、 外側から織り込む手法です。

例えば、ログ出力の機能を考えてみてください。 ログ出力の処理を複数のクラスに同じように記述するのは、 面倒なだけでなく、ログ出力の処理に仕様変更が入った場合に、 複数のクラスを同じように修正しなければいけないという問題がおきます。 つまり、仕様変更に弱いのです。

このような場合に、ログ出力の処理をモジュールとして抽出し、 個々のクラスからは削除します。 そして、コンパイル時もしくは実行時に抽出したモジュールをバイトコードに埋め込むことで、 あたかもソースコードに記述されているかのように実行することができます。

ログ出力の処理に仕様変更が入った場合も抽出したモジュールを修正するだけですみます。 メンテナンス性が向上することがわかっていただけるでしょう。

AOPを考える上でキーとなる概念

Advice(MethodInterceptor)

プログラム中に挿入されるコードを表します。Interceptorと呼ばれることもあります。

Joinpoint(MethodInvocation)

対象となるクラスとAdviceを結合するポイントを表します。 AdviceはJoinpointから引数やメソッドの情報を取得することができます。

Pointcut

どこにJoinpointを設定するのかを定義します。

Aspect

AdvicePointcutを関連付けます。

InterType

フィールドやメソッドの追加、実装するインタフェースの追加など,クラスの静的な構造を変更します。


S2AOPリファレンス


S2AOPで用意している主なInterceptor

TraceInterceptor

クラス名

org.seasar.framework.aop.interceptors.TraceInterceptor

説明

トレース処理を行なうためのInterceptorです。 DateクラスのgetTime()メソッドにTraceInterceptorを適用するための設定ファイルは次のようになります。

<component class="java.util.Date">
    <aspect pointcut="getTime">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>

ThrowsInterceptor

クラス名

org.seasar.framework.aop.interceptors.ThrowsInterceptor

説明

例外を共通的に処理するためのInterceptorです。 使用するにはThrowsInterceptorを継承し、Object handleThrowable(Throwable, MethodInvocation)を実装します。 ThrowableにはThrowableの任意のサブクラスを指定することができます。 handleThrowable()はいくつでも定義することができます。

例えば、NullPointerExceptionをキャッチしてメッセージを出力するには、 次のようなInterceptorを作成します。

package examples.aaa.interceptor;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.ThrowsInterceptor;

public class MyThrowableInterceptor extends ThrowsInterceptor {
    public void handleThrowable(NullPointerException e, MethodInvocation invocation)
        throws Throwable {
        System.out.println("ぬるぽ");
        throw e;
    }
}

ToStringInterceptor

クラス名

org.seasar.framework.aop.interceptors.ToStringInterceptor

説明

toString()メソッドでフィールドの情報を出力するためのInterceptorです。

RemoveSessionInterceptor

クラス名

org.seasar.framework.aop.interceptors.RemoveSessionInterceptor

説明

セッションに格納されているオブジェクトを、メソッドが正常に終了した(例外が発生しなかった)場合に、 自動的に削除するためのInterceptorです。 メソッドにRemoveSessionアノテーションを指定して使います。

次の例では、hoge()メソッドの呼び出しが正常終了した場合に、 セッションに格納されているfooオブジェクトを削除します。

@RemoveSession("foo")
public void hoge() {
    ...
}

InvalidateSessionInterceptor

クラス名

org.seasar.framework.aop.interceptors.InvalidateSessionInterceptor

説明

セッションを、メソッドが正常に終了した(例外が発生しなかった)場合に、 自動的に破棄するためのInterceptorです。 メソッドにInvalidateSessionアノテーションを指定して使います。

次の例では、hoge()メソッドの呼び出しが正常終了した場合に、 HTTPセッションを破棄します。実際には、セッションの破棄はS2Containerの提供するServlet Filter (S2ContainerFilter) によって行われます。

@InvalidateSession
public void hoge() {
    ...
}

DependencyLookupInterceptor

クラス名

org.seasar.framework.aop.interceptors.DependencyLookupInterceptor

説明

getterメソッドに適用されて、 S2コンテナからルックアップしたコンポーネントを返すインターセプタです。

次の例では、getMyService()メソッドを呼び出すとS2コンテナからルックアップされたMyServiceが返されます。

@DependencyLookup
public MyService getMyService() {
    return null;
}

InterceptorChain

クラス名

org.seasar.framework.aop.interceptors.InterceptorChain

説明

複数のInterceptorをグルーピング化し、再利用しやすくします。複数のInterceptorの組み合わせを複数コンポーネントに適用する場合は、InterceptorChainで複数のInterceptorを1つにまとめて、各コンポーネントにはInterceptorChainを指定するようにするといいでしょう。

<component name="interceptor1" .../>
<component name="interceptor2" .../>
<component name="interceptor3" .../>
<component name="chain" class="org.seasar.framework.aop.interceptors.InterceptorChain">
<initMethod name="add"><arg>interceptor1</arg></initMethod>
<initMethod name="add"><arg>interceptor2</arg></initMethod>
<initMethod name="add"><arg>interceptor3</arg></initMethod>
</component> <component ...> <aspect>chain</aspect> </component> <component ...> <aspect>chain</aspect> </component>

InterceptorLifecycleAdapter

クラス名

org.seasar.framework.aop.interceptors.InterceptorLifecycleAdapter

説明

Interceptorはアスペクトをクラスに組み込む際にインスタンス化されるため、singleton以外の場合は意図したとおりに動作しません。
このような場合は、InterceptorLifecycleAdapterをMethodInterceptorのinvoke()メソッドに適用します.

<component name="myInterceptor" instance="prototype" .../>
    <aspect pointcut="invoke">
        <component class="org.seasar.framework.aop.interceptors.InterceptorLifecycleAdapter">
    </aspect>
</component>
<component ...>
    <aspect>myInterceptor</aspect>
</component>

独自実装によるInterceptor

説明

独自にInterceptorを作成する場合は、次のインターフェースまたは、抽象クラスを実装します。

org.aopalliance.intercept.MethodInterceptor
org.seasar.framework.aop.interceptors.AbstractInterceptor

どちらの場合も実装するメソッドは、以下のinvoke()メソッドの1つだけです。

public Object invoke(MethodInvocation invocation) throws Throwable

AbstractInterceptorは、MethodInterceptorをimplementsした抽象クラスです。 AbstractInterceptor.getTargetClass()で、 アスペクトを適用する前のクラスを知ることができます。

MethodInvocationのgetThis()、getMethod()、getArguments()で、 対象となるオブジェクト、メソッド、引数を取得できます。 getThis()でクラス名を取得するとバイトコードで組み込まれたクラス名が取得されます。 proceed()を呼び出すと実際のメソッドが呼び出され実行結果を取得することができます。 実際のメソッドを呼び出す前後にログを出力するInterceptorは次のようになります。

package examples.aaa.interceptor;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;

public class MyInterceptor extends AbstractInterceptor{
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before");
        Object ret = invocation.proceed();
        System.out.println("after");
        return ret;
    }
}

S2AOPで用意している主なInterType

PropertyInterType

クラス名

org.seasar.framework.aop.intertype.PropertyInterType

説明

フィールドに対するgetter / setterメソッドを追加するInterTypeです。
PropertyInterTypeを使うには、S2Tigerが必要です。

注意点

privateのフィールドに対するgetter / setterは作成されません。
フィールドはprotectedにしてください(public でも可)。

<component class="Hoge">
    <interType>
        <component class="org.seasar.framework.aop.interType.PropertyInterType"/>
    </interType>
</component>

PropertyInterTypeを適用するクラスには、getter / setterを追加したいフィールドにPropertyアノテーションを指定します。

import org.seasar.framework.container.annotation.tiger.Property;
import org.seasar.framework.container.annotation.tiger.PropertyType;

public class Hoge {
    @Property
    protected int foo;

    @Property(PropertyType.READ)
    protected long bar;

    @Property(PropertyType.WRITE)
    protected String baz;
}

上の例では、フィールドfooにはgetterとsetterメソッドが、barにはgetterメソッドが、bazにはsetterメソッドが作成されます。

クラスにPropertyアノテーションを指定することもできます。 Propertyアノテーションの指定されていないフィールドへのデフォルトの指定になります。

import org.seasar.framework.container.annotation.tiger.Property;
import org.seasar.framework.container.annotation.tiger.PropertyType;

@Property
public class Hoge {
    protected int foo;

    protected long bar;

    @Property(PropertyType.NONE)
    protected String baz;
}

上の例では、フィールドfooとbarにはgetterとsetterメソッドが作成されますが、bazにはメソッドが作成されません。

InterTypeChain

クラス名

org.seasar.framework.aop.intertye.InterTypeChain

説明

複数のInterTypeをグルーピング化し、再利用しやすくします。複数のInterTypeの組み合わせを複数コンポーネントに適用する場合は、InterTGypeChainで複数のInterTypeを1つにまとめて、各コンポーネントにはInterTypeChainを指定するようにするといいでしょう。

<component name="interType1" .../>
<component name="interType2" .../>
<component name="interType3" .../>
<component name="chain" class="org.seasar.framework.aop.interttype.InterTypeChain">
<initMethod name="add"><arg>interType1</arg></initMethod>
<initMethod name="add"><arg>interType2</arg></initMethod>
<initMethod name="add"><arg>interType3</arg></initMethod>
</component> <component ...> <interType>chain</interType> </component> <component ...> <interType>chain</interType> </component>

独自実装によるInterType

説明

独自にInterTypeを作成する場合は、次のインターフェースまたは、抽象クラスを実装します。

org.seasar.framework.aop.InterType
org.seasar.framework.aop.intertype.AbstractInterType

InterTypeを実装するクラスは、以下のメソッドを実装します。

public void introduce(Class targetClass, CtClass enhancedClass)

targetClassはInterTypeが適用されるクラスです。enhancedClassはInterTypeを組み込むクラスで、先に登録されているInterceptorやInterTypeが適用済みの場合もあります。
CtClassの詳細はJavassistチュートリアルJavaDocを参照してください。

AbstractInterTypeは、InterTypeをimplementsした抽象クラスです。AbstractInterTyeのサブクラスは次のメソッドを実装します。

public void introduce() trhows CannotCompileException, NotFoundException

AbstractInterTypeのサブクラスは、次のメソッドで必要なオブジェクトを取得することができます。

protected Class getTargetClass()
protected CtClass getEnhancedClass()
protected ClassPool getClassPool()

AbstractInterTypeは、フィールドやメソッド,実装するインタフェースを追加するためのユーティリティメソッドを提供します。以下はその一部です。

protected void addField(Class type, String name)
protected void addField(Class type, String name, String init)
protected void addStaticField(Class type, String name)
protected void addStaticField(Class type, String name, String init)
protected void addMethod(String name, String src)
protected void addMethod(Class returnType, String name, String src)
protected void addMethod(String name, Class[] paramTypes, String src)
protected void addMethod(Class returnType, String name, Class[] paramTypes, String src)
protected void addStaticMethod(String name, String src)
protected void addStaticMethod(Class returnType, String name, String src)
protected void addStaticMethod(String name, Class[] paramTypes, String src)
protected void addStaticMethod(Class returnType, String name, Class[] paramTypes, String src)