-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(*)メソッドには、以下の引数バリエーションがあります。
- injectDependency(Object outerComponent)
- injectDependency(Object outerComponent, Class componentClass)
- 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を利用する方法といえるでしょう。