パターンというか、、、、Springで、Guiceっぽくキャスト無しにするための実装方法
Type Safe DI Proxy (タイプセーフなDIプロキシ)パターンによる解決策
Type Safe DI Proxyパターンとは、Genericsを使ってサービスオブジェクトの取得時にキャスト不要にするパターンです。
それ以上の効果を見込んでみたんですが、どうもそれ以上でも以下でもなさそうです。
例として、いつものDIのサンプルを使います。
オブジェクト | 説明 |
---|---|
Service | サービスオブジェクトのインターフェイス |
ServiceImpl | サービスオブジェクトの実装 |
Dependency | 依存性オブジェクトのインターフェイス |
DependencyImpl | 依存性オブジェクトの実態 |
ApplicationStart | サンプルの起動ポイント |
TypeSafeDIProxy | SpringのApplicationContextを仲介し、getBean()メソッドをタイプセーフにするクラス |
サービスオブジェクトと、ディペンデンシィオブジェクトの2つに対して、インターフェイスと具象クラスのペアを用意します。
このサンプルのポイントである、TypeSafeDIProxyクラスから見て行きます。
このクラスは、SpringのApplicationContextを仲介し、getBean()メソッドをタイプセーフにします。
TypeSafeDIProxy
package net.kronos_jp.generics; import org.springframework.context.ApplicationContext; public class TypeSafeDIProxy { ApplicationContext context; public TypeSafeDIProxy(ApplicationContext paContext){ this.context = paContext; } public <T> T getBean(Class<T> beanClass){ return (T)this.context.getBean(beanClass.getSimpleName()); } }
上記のgetBean()の戻り値は、引数で受け取ったClass型との互換性を必要とするようになります。
次に、アプリケーションの起動クラスは以下です。
SpringのコンテキストをTypeSafeDIProxyクラスでラップして生成しています。
getBean()メソッドで、キャストをせずにサービスオブジェクトが取り出せていることに注目してください。
Genericsの機能で、getBeanメソッドの引数に設定した「Service.class」を戻り値の型として制限しているためです。
ApplicationStart
package net.kronos_jp.generics; import org.springframework.context.support.ClassPathXmlApplicationContext; public class ApplicationStart { public static void main(String[] args) { // コンテキストを取得 TypeSafeDIProxy context = new TypeSafeDIProxy( new ClassPathXmlApplicationContext( "net/kronos_jp/generics/application.xml" ) ); // サービスオブジェクトを取得 Service service = context.getBean(Service.class); // インジェクトされた依存オブジェクトのメソッドを実行 service.getDependency().hello(); } }
ためしに、getBean()メソッドの戻り値を受け取る変数にServiceインターフェイスと互換性の無い型をしていするとコンパイルエラーが表示されるのが分かります。
アプリケーションの設定ファイルでは、サービスオブジェクトのIDを、サービスインターフェイスのクラス名「Service」として定義しています。
これにより、Class型のgetSimpleName()メソッドで受け取れるクラス名と一致します。
application.xml
<?xml version="1.0" encoding="SHIFT-JIS"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="Service" class="net.kronos_jp.generics.ServiceImpl"> <property name="dependency" ref="dependency"/> </bean> <bean id="dependency" class="net.kronos_jp.generics.DependencyImpl"> <property name="name" value="yamamoto"/> </bean> </beans>