-S09: CDオートチェンジャー

Seasar2のとてもユニークな機能として、コンポーネント設定がある条件を満たせば自動で初期化をする機能があります。設定XMLに初期化のための具体的な内容を書かなくてもS2Containerがコンポーネントのクラス型をチェックして可能な設定をすべてやってくれるのです。

list09-1. インターフェイス型のプロパティを持つAutoChangerCar.java
package tutorial.org.seasar.console;
public class AutoChangerCar implements Car {
  private BGM bgm;

  public void setBGM(BGM bgm) {
    this.bgm = bgm;
  }

  public void run() {
    System.out.println("BGM is " + bgm.getDisplay());
  }
}

list09-1のAutoChangerCarは、S06のPaintedCarのようにプロパティを持っています。しかしこのプロパティBGMの型はS08で例示したインターフェイスBGMです。

list09-2. プロパティの自動設定をするcar.xml修正版
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.CD">
    <arg>'Qururi'</arg>
    <arg>'Antenna'</arg>
  </component>

  <component class="tutorial.org.seasar.console.AutoChangerCar"/>
</components>

list09-2の設定XMLでは、<component>エレメントではプロパティの設定をしていません。ただし、インターフェイスBGMの実装クラスであるCDをclass属性にもつ<component>エレメントで、コンポーネントが別途登録されていることに注意してください。
この状態でも実行すると結果は以下のとおりです。

BGM is Qururi's [Antenna]

プロパティにちゃんとCDコンポーネントが設定されました。この自動プロパティ設定の要件は以下のとおりです。

  • プロパティの型がインターフェイスである
  • プロパティの型でコンテナ上に取得可能なコンポーネントが登録されている
  • <component>エレメントのautoBinding属性が記述されていないか、"auto"もしくは"property"が値である

プロパティの自動設定は、そのプロパティ型をキーとして内部でS2Container#getComponent(Class)と同じような動作が行われます。よって、プロパティ型はインターフェイスでなくてはならず、またそのインターフェイスを実装したコンポーネントがコンテナに一つ登録されていなければなりません。S05で説明したように、同じインターフェイスを実装するコンポーネントを複数登録しているとこの自動プロパティ設定時にTooManyRegistrationRuntimeExceptionが発生してしまいます。
プロパティ自動設定は、<component>エレメントの記述も要件となってます。<component>エレメントには任意属性のautoBinding属性がありますが、list09-2のようにこの属性自体を記述しないか、値に"auto"もしくは"property"を記述すれば自動設定ができるかどうか型情報のチェックをコンテナが行いはじめます。autoBinding属性のとれる値は以下のとおりです。

  • auto (属性省略時のデフォルト)
  • constructor
  • property
  • none

プロパティの自動設定と同様に、Seasar2コンストラクタも自動設定できます。"auto"はこのコンストラクタ自動設定を行う"constructor"とプロパティ自動設定を行う"property"の両方を有効とした値です。逆に"none"はコンストラクタもプロパティも自動では設定しません。属性省略時はコンテナデフォルトの"auto"となります。
list09-1を変更して、コンストラクタ自動設定の例を見てみましょう

list09-3. コンストラクタ引数を持つAutoChangerCar.java修正版
package tutorial.org.seasar.console;
public class AutoChangerCar implements Car {
  private BGM bgm;

  public AutoChangerCar(BGM bgm) {
    this.bgm = bgm;
  }

  public void run() {
    System.out.println("BGM is " + bgm.getDisplay());
  }
}

設定XMLはlist09-2のままです。すでにデフォルトの"auto"なので、コンストラクタ自動設定も以下の条件が整っているために動作するのです。

実はコンストラクタの自動設定には気をつけなければならない点があります。

  • 複数コンストラクタを持つクラスの場合、インターフェイス引数のみでその数が最大のものを使って自動設定を行う

たとえば、次の場合はコンポーネント取得に失敗します。

// NG
public void AutoChangerCar(BGM bgm)
public void AutoChangerCar(BGM bgm, java.util.Map no_regist)

二番目の引数がインターフェイスMapのために、コンテナはMapを実装したコンポーネントを探し、ComponentNotFoundRuntimeExceptionで失敗します。一方、以下の場合はOKです。

// OK
public void AutoChangerCar(BGM bgm)
public void AutoChangerCar(BGM bgm, String non_interface)

こちらの場合は、二番目の引数がインターフェイスではないために対象から除外され、インターフェイスBGM型の引数を一つもつコンストラクタが選択されるためです。

// NG
public void AutoChangerCar(BGM bgm)
public AutoChangerCar(BGM bgm, Car cyclic)

この場合は、また違う問題が発生します。

org.seasar.framework.container.CyclicReferenceRuntimeException: [ESSR0047]tutorial.org.seasar.console.AutoChangerCarで循環参照が発生しました

二番目の引数で自分を見つけてしまいました。循環参照例外です。自動設定であるなし関わらず、コンポーネント間の依存性で循環参照するとCyclicReferenceRuntimeExceptionをコンポーネントが発生させます。
Seasar2.0.1より、初期化メソッドを利用したコンポーネント設定の場合も、コンストラクタの自動設定とほぼ同様に引数の自動設定機能が追加されました。list08-1のStereoCarを用いた例を、以下のようにします。

list09-4. 初期化メソッドの自動設定をするcar.xml修正版
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component class="tutorial.org.seasar.console.CD">
    <arg>'Qururi'</arg>
    <arg>'Antenna'</arg>
  </component>

  <component class="tutorial.org.seasar.console.StereoCar">
    <initMethod name="addBGM"/>
  </component>
</components>

初期化メソッドの場合は、<initMethod>エレメントを省略することはできません。しかし、初期化メソッドの引数の記述を省略することができます。初期化メソッドは以下の要件で自動設定となります。

  • 初期化メソッドの引数型がインターフェイスである
  • 初期化メソッドの引数型でコンテナ上に取得可能なコンポーネントが登録されている
  • <initMethod>エレメントの子要素として、<arg>エレメントが一つも記述されていない
  • 複数同名のメソッドを持つ場合、インターフェイス引数のみでその数が最大のものを使って自動設定を行う

list09-4では、<arg>エレメントが無い<initMethod name="addBGM"/>が記述されています。これが、StereoCar#addBGM(BGM)メソッドを適用して、引数に自動でコンポーネントを設定します。


Seasar2コンポーネント初期化設定の基本的な項目はこれで終わりです。コンポーネント初期化に関わるより高度な話題は、コンポーネント生成個数を制御する、インスタンスモード、外部コンポーネントへのInject設定、Selを用いたコンポーネントインスタンス化とコンテナ登録などです。これらは基本機能の解説が一巡して後に触れていきます。次回からはAOP機能です。