Spring Framework 入門記 Contextその6 邪悪なSingleton
先ほどの「Context その5」を書き込んでほんの一息ついている間にid:higayasuoさんの日記からリンクが張られていました.あまりの速さにびっくりです.他にも更新から1,2分で(誤字とか修正してたのにぃ)見に来てくれる人がいたりして,皆さんどんな生活をしているのやら.(^^;
それにしてもS2の定義ファイル,すっきりしているのに柔軟そうでいいですね.それに比べると,Springの定義ファイルがいやになるくらいまどろっこしく見えてきてしまいます.このあたりはJavaBeansマンセーなミニマリズム故って感じでしょうか.それでもSpring Framework入門記は続きます.なにせ日記ですから(謎).
さ,本題にはいりましょう.
「3.15. Creating an ApplicationContext from a web application」
をぃをぃ,いきなり話がWEBアプリですか.何でも,ContextLoaderListener
およびContextLoaderServlet
というApplicationContext
を構築するためのクラスが用意されているそうです.ContextLoaderListener
はServlet2.4のServletContextListener
を実装したクラスです.ContextLoaderServlet
はServletContextListener
が使えない環境の場合に使用します.それぞれの場合のweb.xml
の書き方が説明されているのですが,今ここでTomcatとか動かして画面を出そうという気は全然しないので(UI
きらーい),ここはこれで終わりにします.心より恥じる.
「3.16. Glue code and the evil singleton」
ちょっと強引ですが,これで3章も残すところ本節だけとなりました.そーかー,やっぱりSingletonは邪悪かー.ClassLoader
うひゃうひゃだったりすると使いづらかったりすることあるもんねー.
などと早とちりをしてしまったのですが,そういう意味ではないようです.ここで説明されているのは,BeanFactory
やApplicationContext
をSingletonとして使うということのようです.いけませんねー,悪の枢軸(axis of evil)のおかげで"evil"ってとっても悪いことを指すように思いこんじゃってました.どっちかというと,「シングルトンの化身」くらいの意味?
なぜこういうものが必要かというと,コンテナの外でインスタンス化されたオブジェクトからコンテナを使いたい場合があるから,ということです.世の中にはすでに様々なフレームワークがあって,インスタンス化はそれらにおまかせというのはIoCなコンテナに限った話ではありません.そのようなフレームワークをいろいろと組み合わせて使う場合,あっちでインスタンス化されたオブジェクトからこっちのコンテナでDependency Injectionされたオブジェクトを使いたいということもあるでしょう.そのような場合のために,Spring Frameworkが用意してくれているのがBeanFactoryLocator
です.だから"Glue code"なんですね.BeanFactoryLocator
はインタフェースなのですが,次の実装クラスが用意されています.
SingletonBeanFactoryLocator
BeanFactory
をSingletonのように取得できるBeanFactoryLocator
です.ContextSingletonBeanFactoryLocator
ApplicationContext
をSingletonのように取得できるBeanFactoryLocator
です.
だーかーらー,名前が長いんだよー.
なお,これらはSingletonと名付けられているのですが,実際のイメージはSingletonというよりService Locatorに近いかも.実はこいつら,キーを指定すると複数のコンテナを返してくれるのです.Singletonという名称は,先の実装クラスがgetInstance()
でコンテナを返してくれるstaticメソッドを持っているためだと思われます.
これらのBeanFactoryLocator
はXMLファイルからコンテナをインスタンス化して返してくれるのですが,そのXMLファイルのパスなどはこれまたXMLファイルで与えます.他に,JNDIからlookupして取得したパスで示されるBean定義XMLファイルでインスタンス化したコンテナを返してくれる,JndiBeanFactoryLocator
およびContextJndiBeanFactoryLocator
も用意されています.
いずれにせよ重要なのは,BeanFactoryLocator
達は何とかしてBeanFactory
を返してくれるということで,イメージ的にはBeanFacotry
のファクトリです.これを覚えておきましょう.はいっ!*1
これらのBeanFactoryLocator
が返してくれるのはBeanFactory
そのものではなく,BeanFactoryReference
です.これは,BeanFactory
の参照を返すgetFactory()
というメソッドを持つインタフェースです.ややこしいですね.これを使うことで,コンテナが本当に必要とされるまでコンテナの初期化を遅延したり,コンテナへの参照を持つだけのためメモリ使用量が少ないBeanFactoryReference
のインスタンスをセッションに保存できたりして便利,ということのようです.
能書きはこれくらいにして,実際に動かして確認することにしましょう.でないと,わけがわからなくなりそうです.
なんといっても今回の副題は「Context その6」ですから,BeanFacotry
ではなくApplicationContext
を使わなくてはなりません.ということで,ContextSingletonBeanFactoryLocator
を使います.
そのContextSingletonBeanFactoryLocator
ですが,定義ファイルのデフォルト名を定数で持っています.その名を"beanRefContext.xml"
といいます.ちなみにSingletonBeanFactoryLocator
の場合は"beanRefFactory.xml"
です.そんなわけで,"beanRefContext.xml"
を作成しましょう.その内容は,このBeanFactoryLocator
から取得するコンテナの定義です.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="context" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg> <list> <value>factory.xml</value> <value>beans.xml</value> </list> </constructor-arg> </bean> </beans>
なるほどー,コンテナもまた<bean>
要素で定義するんですねー.ここでは,"context"
という名前のApplicationContext
を定義しています.このApplicationContext
の定義ファイルとして,これまでと同様facotry.xml
とbeans.xml
をコンストラクタの引数として与えています.
そのbeans.xml
の内容は次の通りです.いつものように<bean>
要素のみ抜粋.
<bean id="foo" class="study.Foo"> <property name="text"><value>evil singleton</value></property> </bean>
なお,今回のFoo
はtext
というString
のプロパティを持っています.
そして実行用のクラスなんですが,次のようになりました.
package study; import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.beans.factory.access.BeanFactoryReference; import org.springframework.context.ApplicationContext; import org.springframework.context.access.ContextSingletonBeanFactoryLocator; public class Main { public static void main(String[] args) { try { BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(); BeanFactoryReference ref = locator.useBeanFactory("context"); ApplicationContext context = (ApplicationContext) ref.getFactory(); Foo foo = (Foo) context.getBean("foo"); System.out.println(foo.getText()); ref.release(); } catch (Exception e) { e.printStackTrace(); } } }
最初にSingletonなやり方でBeanFactoryLocator
を取得します.ここでは引数を指定していないので,デフォルトのXMLファイルパス名である"beanRefContext.xml"
が使用されます.そして,context
という名称でBeanFactoryReference
を取得します.そこからApplicationContext
を取得します.後はいつものようにBeanを取得できます.
最後に,BeanFactoryReference
のrelease()
メソッドを呼び出すことで,コンテナの後処理(destroySingletons()
)が実行されます.
これを実行すると,
evil singleton
と表示されました.やれやれ.
今回のBeanFactoryLocator
は,結構奥が深そうです.J2EEなどSpring Framework以外の様々なコンテナやClassLoader
階層などを駆使する場合に,ここに行き着くことになるのかもしれません.今のところ十分に理解できたというわけではないのですが,非常に重要な存在っぽいということだけ忘れないことにして(それが難しい!),これで3章を終わりにしたいと思います.
さぁ,次は4章へ行くぞっ! おー!!
*1:凛ちゃん風