Springフレームワークを利用して、Javaのライブラリを開発しています。
ちょっと変わった構成で利用することが想定されており、特にDIについて困っていました。
- ライブラリを利用するのは既存のアプリケーション(コード改変不可)
- 既存アプリケーションには様々なイベントが用意されている
- 上記イベントと当該ライブラリのクラス/メソッドを関連付けることが可能
- ライブラリのメソッド起動方法はリフレクションを利用している
問題だったのは、メソッド起動方法で、よくあるリフレクションを利用してメソッドが起動されているところです。この方法だと、クラスはnewを利用してインスタンス生成される為、含まれるフィールドがDIされませんでした。
[java] // 既存アプリケーションがライブラリを実行する直前の処理↓ public class LegacyAppMain { public void execute() { Method method = HogeClass.class.getMethod("hogeMethod", ...); method.invoke(clazz.newInstance(), ...); } }
// ライブラリ public class HogeClass { @Autowired HogeService hogeService; // DIされない
public void hogeMethod() {
hogeService.hugaMethod(); // ヌルポ
}
} [/java]
LegacyAppMainクラスは改変不可の為、newによるインスタンス生成は避けられません。 まず、Autowiredアノテーションを利用しないでインスタンス生成したクラスのフィールドがDIされる為には、以下のようにApplicationContextからインスタンスを生成する必要がありました。
まずはスキャン対象のコンポーネントをXMLに定義します。 [xml] <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context ">http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="hoge.huga" /> <!-- このパッケージ配下にHogeServiceが含まれている --> </beans> [/xml]
ApplicationContextの生成は上記のXMLを指定します。 [java] ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:./META-INF/exp/applicationContext.xml"); HogeClass hoge = ClassapplicationContext.getBean(HogeClass.class); hoge.hogeMethod(); // hogeServiceがちゃんとDIされる為、ヌルポにならない [/java]
この方法も知らなかったため、かなりハマってしまいました。 さらに、既存アプリからの呼び出しの後に工夫しなければいけない為、さらにハマりました。 考えた結果、ひとつクラスをラップするようにしました。 [java] // ライブラリ public class HogeClass { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:./META-INF/exp/applicationContext.xml");
public void hogeMethod() {
Method method = HogeChild.class.getMethod("hogeMethod", ...);
method.invoke(applicationContext.getBean(HogeChild.class), ...);
}
}
public class HogeChild { @Autowired HogeService hogeService; // DIされる
public void hogeMethod() {
hogeService.hugaMethod(); // ヌルポにならない
}
} [/java]
このように呼び出しの階層をすこしずらしたりすることが、パズルを組み合わせていくような感覚でプログラミングのおもしろいところかな、なんて思ったりしました。