Springと連携したサンプルがJDaveにあって(といってもJavadocコメントに直書きだけど)。
こんな感じ。
public class MySpecification extends Specification<Something> { private SomeInjectedField field; public MySpecification() { new InjectionSupport().inject(this, new SpringInjector()); } public class SomeContext { ... } } public class SpringInjector implements IFieldInjector { private BeanFactory factory = new ClassPathXmlApplicationContext("/myAppContext.xml"); public void inject(Field field) { if (factory.containsBean(field.field().getName())) { field.set(factory.getBean(field.field().getName())); } } }
テストクラスのコンストラクタで、自分自身とIFieldInjectorを引数にInjectionSupport#inject()メソッドを呼び出す。
IFieldInjectorにはinject()メソッドだけが定義されてる。
で、DIはSpringの設定ファイルからファクトリを作って、テストクラスのフィールド名をBean名とみなしてコンテナから取り出し、セットしてるだけ。
public interface IFieldInjector { void inject(Field field); }
このFieldクラスはjava.lang.reflect.FieldをラップしたJDaveのFieldクラス。
InjectionSupportはこんなの。
public class InjectionSupport { public void inject(Object object, IFieldInjector injector) { Class<?> type = object.getClass(); do { List<Field> fields = fieldsFor(object, type); for (Field field : fields) { injector.inject(field); } } while ((type = type.getSuperclass()) != null); } }
InjectionSupportクラスは引数で渡されたクラスのフィールドを取り出して、IFieldInjector#inject()メソッドに渡してる。
Seasar2版のIFieldInjectorを作ってみた
package jyukutyo.sample; import jdave.injection.Field; import jdave.injection.IFieldInjector; import org.seasar.framework.container.S2Container; import org.seasar.framework.container.factory.S2ContainerFactory; public class Seasar2Injector implements IFieldInjector { private S2Container container; public Seasar2Injector() { S2ContainerFactory.configure("test.dicon"); container = S2ContainerFactory.getConfigurationContainer(); } public void inject(Field field) { String fieldName = field.field().getName(); if (container.hasComponentDef(fieldName)) { field.set(container.getComponent(fieldName)); } } }
Springと同じように設定ファイルを読み込んで、フィールド名をコンポーネント名とみなしてセットするだけ。
設定ファイルはコンポーネントの自動登録を利用した。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> <components> <component class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister"> <property name="instanceDef"> @org.seasar.framework.container.deployer.InstanceDefFactory@SINGLETON </property> <property name="autoNaming"> <component class="org.seasar.framework.container.autoregister.DefaultAutoNaming"/> </property> <initMethod name="addClassPattern"> <arg>"jyukutyo.sample"</arg> <arg>".*Impl"</arg> </initMethod> <initMethod name="registerAll" /> </component> </components>
ポイントは「
AbstractAutoRegister#registerAll()はWebアプリケーションだったらデプロイ時にSeasar2が呼び出してくれてるのかな。。。
ちょっとどこだかわからかったけど、テストでは自分で呼び出しておく必要がある。
InstanceDefFactoryは今回ステートレスなオブジェクトをDIするでSINGLETONにしてるけど、実際は普通PROTOTYPEだろう。
でもPROTOTYPEだと毎回生成するから、テストが多くなったらコストがかかるかな。。。?
このへんテストのコンテキストでDIするインスタンスの生成について考えないといけないかも。
今はフィールド名からコンポーネントを取得してるけど、せっかくSeasar2だしAutoNamingの規約を使えば、DIするクラス(インタフェース)の型からうまくやれるかも。