-S18: 自動車工場

instanceモードが"outer"の時の動作を見ていきます。

list18-1. オブジェクト生成と利用を行うFactory.java
package tutorial.org.seasar.console;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
public class Factory {
  private static String PATH = "tutorial/org/seasar/console/car.xml";
  public static void main(String[] arg) {
    S2Container container = S2ContainerFactory.create(PATH);
    Car car = new UnderConstructionCar();
    car.run();
    System.out.println("inject!");
    container.injectDependency(car);
    car.run(); 
  }
}

list18-01ではこれまで利用してきたHighwayクラスによるブートストラップではなく、新たにFactoryクラスを作成しています。はじめにCarインターフェイスを実装したUnderConstructionCarを生成し、そのままrun()メソッドを実行しています。その後に、S2Container#ingectDependency(Object)を実行して、再度run()メソッドを実行します。

list18-2. InjectされるUnderConstructionCar.java
package tutorial.org.seasar.console;
public class UnderConstructionCar implements Car {
  private String engine = "none";
  public void setEngine(String engine) {
    this.engine = engine;
  }

  public void run() {
    System.out.println("engine is '" + engine + "'");
  }
}

list18-2には特筆すべき論点はありません。Type2初期化を期待する普通のコンポーネントです。

list18-3. Inject dependency を行うcar.xml
<?xml version="1.0" encoding="UTF-8"?>
<components>
  <component instance="outer"
    class="tutorial.org.seasar.console.UnderConstructionCar">
    <property name="engine">'SOHC-V12 Twin turbo'</property>
  </component>
</components>

list18-3では<component>のinstance属性において"outer"を指定しています。実行結果は以下のとおりです。

engine is 'none'
inject!
engine is 'SOHC-V12 Twin turbo'

実行結果の1行目は、S2Container#ingectDependency(Object)の実行前です。UnderConstructionCarのEngineプロパティは初期値のままです。3行目はS2Container#ingectDependency(Object)の実行後です。こちらでは設定XMLに記述したとおりにプロパティ設定されてます。
outerの場合には、S2Containerがコンポーネントを生成しません。よってinstanceモードがouterのコンポーネントを今までのように、S2Container#getComponent(*)で取得しようとすると、UnsupportedOperationException例外が発生します。outerは、コンテナ外部でコンポーネント生成を行い、S2Container#injectDependency(*)メソッドを用いて、プロパティ等の初期化だけを行います。S2Containerの初期化機能は、Type2(プロパティの初期化)、Type3(コンストラクタによる初期化)、Type4(メソッドによる初期化)がありますが、生成が外部のため、outerではType2およびType4の機能のみが利用できます。すなわち、<component>エレメントの子要素として有効に機能する子エレメントは、<property>と<initMethod>です。
S2Container#injectDependency(*)メソッドには、以下の引数バリエーションがあります。

  1. injectDependency(Object outerComponent)
  2. injectDependency(Object outerComponent, Class componentClass)
  3. injectDependency(Object outerComponent, String componentName)

1番目は、内部でinjectDependency(outerComponent, outerComponent.getClass())としていて、2番目と同じ動作をします。3番目は設定XML中の該当するコンポーネント定義の検索に、<component>エレメントのname属性を用いて行うメソッドです。例では以下のような修正です。

container.injectDependency(car, "S600L");

<component name="S600L" instance="outer">

injectDependency(Object, String)を用いて初期化する場合には、<component>エレメントにclass属性は任意です。injectDependency(*)でも自動でプロパティを検出して設定する機能がありますが、class属性が設定されていないと、第一引数のコンポーネントインスタンスのプロパティを調べて自動設定を行います。class属性が設定されているときには、属性値のクラスが持つプロパティを走査します。

container.injectDependency(car, "S600L");

<component name="S600L" instance="outer"
   class="tutorial.org.seasar.console.AutoChangerCar">

<component>エレメントにclass属性が設定されている際に、属性値にキャスト不可のコンポーネントを初期化しようとすると、例外が生成されます。

org.seasar.framework.container.ClassUnmatchRuntimeException:
[ESSR0069]定義されたクラス(tutorial.org.seasar.console.AutoChangerCar)に実際のクラス(tutorial.org.seasar.console.UnderConstructionCar)を適用できません

外部のすでに完成したフレームワーク等との連携の際には、S2Container上で管理するコンポーネントを外部フレームワークで利用する最も緩やかな結合を行うのがouterです。outerの場合には、<aspect>も利用不可能ですが、既存の変更が最も少なく、Seasar2を利用する方法といえるでしょう。