-S11: ABSの仕組み

さて、サンプルのlist10-3を以下の設定XMLに修正して実行すると結果は違います。

list11-1. aspectを除いたcar.xml修正版
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.BrakingCar">
  </component>
</components>

結果は以下のとおりです。

Brake!

これはBrakingCarの出力のみです。<aspect>エレメントを追加することによって、list10-2のABSが追加的な出力を行っていたのです。<component>の子要素として<aspect>エレメントを記述すると、その<component>が定義するコンポーネントクラスの動作を変えることができます。S2Containerから取得されたコンポーネントのメソッドコールがなされる際、<aspect>エレメントに定義されるSeasar2のインターフェイスAroundAdvice実装クラスがそのメソッドコールを途中で横取りします。Highwayクラス(list03-1)がICar#run()とコールしているところ、ABS#invoke(Joinpoint)が実行されます。これは、S2Containerが<aspect>エレメントによって追加されたAroundAdvice実装クラスのinvoke(Joinpoint)メソッドをコンポーネントの各メソッドの代わりに呼ぶようにバイトコードエンジニアリングによって動的に継承クラスを作って実現しています。list10-2を順に見ていきます。

  public Object invoke(Joinpoint joinpoint) throws Throwable {
    System.out.println("Ooops, danger!");

BrakingCarのrun()メソッドが呼ばれると、横取りされてこのABS#invoke(Joinpoint)が実行されます。ここでまず、"Ooops, danger!"と出力しています。そのため、結果の一番初めにこの文字列が出力されることになります。

    Object ret = joinpoint.proceed();

次にメソッドの引数に与えられているJoinpoint#proceed()を呼びます。このJoinpointは内部に本来実行すべきメソッドおよび引数情報が保存されていて、リフレクションにより正しくBrakingCar#run()を呼び出します。例では結果の二行目にメソッドの返値を文字列出力しています。"Brake!"というのがそれにあたります。

    System.out.println("kikikiki...");
    return ret;
  }

最後に、結果の三行目の"kikikiki..."を出力しています。
では、もう一段複雑な例を見てみましょう。

list11-2. 追加するAdviceクラスNavigator.java
package tutorial.org.seasar.console;
import org.seasar.framework.aop.AroundAdvice;
import org.seasar.framework.aop.Joinpoint;
public class Navigator implements AroundAdvice {
  public Object invoke(Joinpoint joinpoint) throws Throwable {
    System.out.println("Ah! Cat on the way!");
    Object ret = joinpoint.proceed();
    System.out.println("Ouch, I have blown the tongue.");
    return ret;
  }
}

list11-2の内容は、出力文字列が異なるほかABSと変わりません。

list11-3. <aspect>を二つ登録したcar.xml修正版
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.BrakingCar">
    <aspect pointcut="run">
      <component class="tutorial.org.seasar.console.Navigator"/>
    </aspect>
    <aspect pointcut="run">
      <component class="tutorial.org.seasar.console.ABS"/>
    </aspect>
  </component>
</components>

list11-3ではさらに<aspect>を追加して、Navigatorの登録を増やしました。この実行結果は、

Ah! Cat on the way!

Ooops, danger!

Brake!

kikikiki...

Ouch, I have blown the tongue.

です。設定XMLの二つの<aspect>エレメントの出現順序を変えると、実行結果も変わります。

list11-4. <aspect>の順序を二つ登録したcar.xml修正版
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.BrakingCar">
    <aspect pointcut="run">
      <component class="tutorial.org.seasar.console.ABS"/>
    </aspect>
    <aspect pointcut="run">
      <component class="tutorial.org.seasar.console.Navigator"/>
    </aspect>
  </component>
</components>

結果は、

Ooops, danger!

Ah! Cat on the way!

Brake!

Ouch, I have blown the tongue.

kikikiki...

こうなります。