Lookup Method Injection による、ソースコードからDIコンテナへの依存の解消

DIコンテナのメリット

Spring Framework を用いて、Serivce や Entity を分離しているソースを見ることが最近は多くなりました。DIコンテナには、各クラスが他のクラスの実装に依存しないですむというメリットのほかに、アプリケーションのソースコードからコンテナへの依存を避けるというメリットがあります。しかし、こんなコードを見たことはないでしょうか。

(Command)this.appCtx.getBean("command");

このコードは、SpringFrameworkに依存しています。ちょっともったいないですよね。以下に、このような場合のソースを全体的に俯瞰してみます。

コンテナ実装に依存したコードの例

エンティティインタフェース
public interface Command {
	void setState(Object state);
	Object execute();
}
エンティティ実装
public class DummyCommand implements Command {
	public Object execute() { return null; }
	public void setState(Object state) {}
}
サービスインタフェース
public interface CommandManager {
	Object process(Object commandState);
}
サービス実装
// ApplicationContextAware が spring framework に依存
public class SpringAwareCommandManagerImpl implements CommandManager, ApplicationContextAware {

	// ApplicationContext が spring framework に依存
	private ApplicationContext appCtx;

	// ApplicationContext に依存
	public void setApplicationContext(ApplicationContext appCtx) throws BeansException {
		this.appCtx = appCtx;
	}

	// appCtx.getBean() で spring framework に依存
	protected Command createCommand() {
		return (Command)this.appCtx.getBean("command");
	}
	public Object process(Object commandState) {
		Command command = createCommand();
		command.setState(commandState);
		return command.execute();
	}
}
bean 定義ファイル
<beans>

  <!-- Command インタフェースの実装 -->
  <bean id="command" class="DummyCommand" scope="prototype"/>
  
  <!-- spring framework に依存した Service の例 -->
  <bean id="springAwareCommandManager" class="SpringAwareCommandManagerImpl"/>
  
</beans>

Lookup Method Injection

このようなケースでは、Lookup Method Injection を用いることにより、サービス実装のソースコードからコンテナへの依存関係を解消することができます。

SpringFramework に依存しないサービス例
// SpringFramework の ApplicationContextAware に依存しない。
abstract public class CommandManagerImpl implements CommandManager {

	// SpringFramework の getBean() に依存しない。
	abstract protected Command createCommand();

	public Object process(Object commandState) {
		Command command = createCommand();
		command.setState(commandState);
		return command.execute();
	}
	
}
bean 定義ファイル
<beans>

  <!-- Command インタフェースの実装 -->
  <bean id="command" class="DummyCommand" scope="prototype"/>
  
  <!-- spring framework に依存しない Service の例 -->
  <bean id="commandManager" class="CommandManagerImpl">

    <!-- この定義で動的にルックアップメソッドを指定することにより、
    プログラム側はSpringFramework に依存しないですむ。  -->
    <lookup-method name="createCommand" bean="command"/>

  </bean>
  
</beans>

最後に

頭痛と吐き気でほとんど眠れんかった。もう朝か o...rz