Spring Framework 入門記 Beanその10 FactoryBean
今回は,昨日の学習中に「毛色が違う」とすっ飛ばしてしまった「3.4.3 FactoryBean」について学習します.
FactoryBean
というのは,自身がFactoryであるようなBeanということです.言い換えると,Beanとして扱うことのできるFactoryですね.このようなものがなぜ必要なのかというと,インスタンスの生成をコンテナにやってもらうのではなく,その外で用意して,それをDependency Injectionの輪に加えたい場合でしょう.
FactoryBean
インタフェースには,次の3つのメソッドが宣言されています.
Object getObject()
- オブジェクトを返します.
Class getObjectType()
- この
FactoryBean
が返すオブジェクトの型を返します. boolean isSingleton()
- この
FactoryBean
が返すオブジェクトがsingletonならtrue
を返します.
簡単そうですね.
それでは例として,ThreadLocal
で保持しているスレッド固有の情報をSpringにDependency Injectionしてもらうサンプルを作ってみます.とはいってもサンプルなので,ちょっと手抜きしてスレッド固有の情報はただの文字列ということにします.
まずはFactoryBean
の実装です.これ自身はSpringで扱われる普通のBeanですから,プロパティを持つことができます.ということで,ThreadLocal
をプロパティとして,そこからget()
したオブジェクト(今回はString
)を自分のgetObject()
の戻り値になるようにします.この文字列はそのときの状況で変わりうるので,Singletonではありません.
ということで,こんな感じ.
package study; import org.springframework.beans.factory.FactoryBean; public class ThreadLocalString implements FactoryBean { ThreadLocal threadLocal; public ThreadLocal getThreadLocal() { return threadLocal; } public void setThreadLocal(ThreadLocal local) { threadLocal = local; } public Object getObject() throws Exception { return threadLocal.get(); } public Class getObjectType() { return String.class; } public boolean isSingleton() { return false; } }
では,これを使用した定義ファイルを作成します(<bean>
要素のみ抜粋).
<bean id="threadLocal" class="java.lang.ThreadLocal"> </bean> <bean id="threadLocalString" class="study.ThreadLocalString"> <property name="threadLocal"><ref bean="threadLocal"/></property> </bean> <bean id="foo" class="study.Foo" singleton="false"> <property name="text"><ref bean="threadLocalString"/></property> </bean>
ここでのポイントは,foo
という名前を持ったBeanのtext
プロパティはコンテナが設定してくれるのですが,その値(文字列)はthreadLocalString
というFactoryBean
が返す値だということです.
最後に実行用のクラスを作ります.
package study; import java.io.FileInputStream; import org.springframework.beans.factory.xml.XmlBeanFactory; public class Main { public static void main(String[] args) { try { XmlBeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml")); ThreadLocal threadLocal = (ThreadLocal) factory.getBean("threadLocal"); threadLocal.set("Midori"); Foo foo = (Foo) factory.getBean("foo"); System.out.println(foo.getText()); threadLocal.set("Saeko"); foo = (Foo) factory.getBean("foo"); System.out.println(foo.getText()); } catch (Exception e) { e.printStackTrace(); } } }
この実行用クラスでは,foo
を取得する前に,ThreadLocal
に文字列を設定しています.そのため,取得したfoo
のtext
プロパティには,その時それぞれの文字列が設定されているはずです.
それでは実行します.
- Loading XML bean definitions from (no description) - Creating shared instance of singleton bean 'threadLocal' - Creating shared instance of singleton bean 'threadLocalString' Midori Saeko
いいねぇ*1.
という感じなんですが,惜しいことにFactoryBean
が返したオブジェクトはDependency Injectionしてもらえないんですよね.無念だ.
大して難しい話じゃないと思うのですが,現状はDependency Injectionした後に,そのオブジェクトがたまたまFactoryBean
だったらgetObject()
するという流れになっているようで,そこで再度Dependency Injectionするようにはなっていないのですね.DTD的にも,FactoryBean
のプロパティとそれが返すオブジェクトのプロパティを区別する書き方はできませんし,しょうがないとあきらめましょう.ただ... 無念だ.