lookup method injection(Spring1.2)
Springではbeanのメンバとしてbeanを設定することができる.しかし,含有する側のbeanをSingleton,含有される側のbeanを非Singletonとしたい場合,含有される側をnewするキックをどうするかという問題が発生する.Springではlookup method injectionという仕組みによって,これを容易に実現する方法を提供しているようなので,試してみた.
以下はbeans.xmlを考えてみる.
上記beans.xmlにおいてidがtopBeanType1であるbeanは,idがinnerBeanType1であるbeanを含有している.
今まで見てきたサンプルの通り,上位コードからBeanFactoryを使用してtopBeanType1を取得する際に,innerBeanType1を生成し,topBeanType1のメンバinnerBeanoとして設定する.
デフォルト設定によりtopBeanType1はSingletonなので,上位コードがBeanFactoryから二回目以降topBeanType1を取得する際には,上は実行されない.
BeanFactoryはすでにnewされているtopBeanType1を返す.
これだとinnerBeanType1は設定ではSingletonではないのに,newされるきっかけが無くなってしまう.
つまり仕様上あるいは設計上innerBeanType1をSingletonとしたくないのに,結果的にSingletonとなってしまう.
これに対処するために,Springでは三つの方法がある.
- 含有する側のbeanのgetterを工夫する.
- getterが呼び出される度にBeanFactoryから含有beanを取得して返すようにする. beanが何故かBeanFactoryに依存してしまい,また依存(含有)関係もハードコードされてしまい,あまりよい方法ではない.
- BeanFactoryAwareを実装する
- BeanFactoryAwareはBeanFactoryのラッパーのように振舞うIntefaceである. 上位プログラムはこの実装クラスからtopBeanType1を取得するようにし,BeanFactoryAwareの実装クラスは,BeanFactoryからtopBeanType1とinnerBeanType1を取得し,後者を前者にsetし上位に返す.
- lookup method injection
- beans.xmlの設定により,SpringにtopBeanType1からinnerBeanType1を取得する方法を指示する.
本頁のサンプルのbeans.xmlを以下に示す.
ポイントはidがtopBeanType1である要素beanの子要素lookup-methodである.
要素lookup-methodは該当beanが含有するbeanをみつけるためのメソッドの名前を指定する.
要素lookup-methodは空要素で,二つの省略可能属性がある(※).
名前の通り,AbstractTopBeanはabstractクラスである.
そして,上で記述したlookup methodをabstractメソッドとしている.
- name
- beanを見付けるためのメソッド名. このメソッドは引数なしでなければならない.
- bean
- 上記メソッドが見付けるbeanまたはその祖先.
protected abstract InnerBean createInnerBean();そしてinnerBeanの取得メソッドは,上記lookup methodへ処理を委譲している.
public InnerBean getInnerBean() {
return createInnerBean();
}
ポイントはここである.
beanの中にBeanFactory云々という処理をいれることなく,これだけの記述でBeanFactoryからinnerBeanType1を取得することができる.
SpringはCGLIBを使用してAbstractTopBeanのサブクラスを動的に生成し,そのサブクラスでcreateInnerBeanの実装を実現する.
すなわちBeanFactoryからidをtopBeanType1を指定して取得できるのは,AbstractTopBeanのインスタンスではなく(abstractだから当然なのだが),Springが自動生成した子クラスである.
サンプルを実行すると,BeanFactoryから取得したログを以下に示す.
2005-05-14 22:35:40,257 INFO [main] xml.XmlBeanDefinitionReader:132 - Loading XML bean definitions from URL [jar:file:/home/matsu/programing/java/spring/dicontainer9/springsample.jar !/org/fireproject/springsample/beans.xml] 2005-05-14 22:35:40,349 INFO [main] support.AbstractBeanFactory:218 - Creating shared instance of singleton bean 'topBeanType1' 2005-05-14 22:35:40,832 INFO [main] springsample.HelloBeanFactory:23 - ====org.fireproject.springsample.AbstractTopBean$$EnhancerByCGLIB$$301af351[] 2005-05-14 22:35:40,858 INFO [main] springsample.HelloBeanFactory:26 - ====org.fireproject.springsample.InnerBeanType1[innerValue=10;] 2005-05-14 22:35:40,860 INFO [main] springsample.HelloBeanFactory:28 - ====org.fireproject.springsample.InnerBeanType1[innerValue=10;] 2005-05-14 22:35:40,860 INFO [main] springsample.HelloBeanFactory:29 - innerBean1 == innerBean2 ? falseこのログ中の
AbstractTopBean$$EnhancerByCGLIB$$301af351が自動生成されたAbstractTopBeanのサブクラスのthis.getClass().getName()の値である. また,今回はgetInnerBeanに対してsetInnerBeanがないことにも注意. Spring的には上記beans.xmlに対してlookup methodで指定したメソッドがあればよく,innerBeanに対するgetterもsetterも必要ではない. getterは上位が必要だから用意した.
※ DTDには以下となっていたが,機能,意味を考えるとREQUIREDだと思う.
<!ATTLIST lookup-method name CDATA #IMPLIED>
<!ATTLIST lookup-method bean CDATA #IMPLIED>
実際これらの属性を省略すると例外となる.
以下はサンプルのドライバクラスである.
このように上位のコードからは,内部でlookup methodが使用されているといったことは考えなくてよい作りになっている.
また,BeanFactoryからidをtopBeanType1を指定して取得したbeanから二度getInnerBeanしてそれを比較(==)している.
この結果は上記ログのとおりfalseであり,うまくlookup method injectionが働いていることが分かる.

