-S17: 走行距離メータ

コンポーネントの生成に関する話題として、S2Container上のinstanceモードによるコンポーネント生成方法の違いについての問題を取り扱います。

list17-1. 走行距離を示すMillageCar.java
package tutorial.org.seasar.console;
public class MillageCar implements Car{
  private int millage = 0;
  public void run() {
    millage += 100;
    System.out.println(millage + "km run.");
  }
}

list17-1ではrun()メソッドにおいてクラスメンバであるint型の"millage"を参照しています。メソッド実行時にカウントアップして走行距離を増加させている点に注意ください。

list17-2. Highway.java修正版
package tutorial.org.seasar.console;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
public class Highway {
  private static String PATH = "tutorial/org/seasar/console/car.xml";
  public static void main(String[] args) {
    S2Container container = S2ContainerFactory.create(PATH);
    Car car = null;
    for(int i = 0; i < 5; i++) {
      car = (Car)container.getComponent(Car.class);
      car.run();
    }
    container.destroy();
  }
}

list17-2ではコンポーネントを呼び出して用いるクライアント側の処理を変更しています。forループによって複数回のコンポーネント取得およびメソッドコールを行っています。

list17-3. car.xml修正版。デフォルトで「singleton」である
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.MillageCar">
    <destroyMethod>out.println('finish.')</destroyMethod>
  </component>
</components>

list17-3では設定XMLにMillageCarコンポーネントを登録し、かつ終了化メソッドとして<destroyMethod>エレメントボディにSelを書く方法で文字列「finish.」を出力しています。実行結果は以下のとおりです。

100km run.
200km run.
300km run.
400km run.
500km run.
finish.

MillageCarコンポーネント取得およびrun()メソッドの呼び出しごとに、走行距離が増加して表示されています。また、最後に<destroyMethod>エレメントによる出力も表示されています。しかし、この設定XMLの<component>エレメントにinstance属性を追加し、値を「prototype」とすると様子が違います。

<component class="tutorial.org.seasar.console.MillageCar" instance="prototype">

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

100km run.
100km run.
100km run.
100km run.
100km run.

毎回のrun()メソッドの出力が一様に一回目の値で出力され、かつ終了化メソッドが効いていません。
<component>エレメントのinstance属性は省略時のデフォルトは「singleton」です。singletonモードではS2Containerは<component>エレメント毎にコンポーネントを一つだけ生成し保持しています。S2Container#getComponent(*)によってコンポーネントの要求があった場合、常に保持している唯一のコンポーネントを返します。よって、MillageCarのメンバーであるmillageは呼び出される度に加算されていき、出力でもその変化が見て取れました。一方で、「prototype」に値を設定すると、S2Containerはコンポーネントの要求がある度に新規にコンポーネントインスタンスを生成して返します。よってそのコンポーネントはコンテナで保持されません。そのために、prototypeモードを設定した際の例の実行結果では、常にコンポーネントにとって初めてrun()メソッドが呼ばれている状態のため、「100km」という一度目にメソッドが呼ばれた際に出力する値となってしまうのです。
singletonモードではコンテナにインスタンスが保持され、prototypeモードでは保持されないという違いは、終了化メソッドについての動作の違いにも現れています。S2Container#destroy()メソッドによって発火するコンポーネントの終了化メソッドは、コンテナ上に保持されるものにだけ適用されます。よって、singletonモードでは<destroyMethod>エレメントの設定が動作し、prototypeモードでは動作していないのです。

instanceモードとコンテナの挙動の変化






















instance\ コンポーネント生成 コンポーネント保持
singleton S2Container S2Containerのため終了化メソッド有効
prototype S2Container クライアント
outer クライアントのためclass属性が任意 クライアント