Spring Framework 入門記 Contextその1 ApplicationContext

今回は「3.9 Introduction to the ApplicationContext」です.長かったbeansパッケージをついに卒業して,ここからはcontextパッケージです.進歩するって気持ちいいですね〜.というわけで,improvement.jpなんていうマイドメインをメールアドレスに使っています.
さてcontextパッケージですが,これはbeansパッケージの上位に位置付けられて,BeanFactoryの機能をframework-styleなアプローチに与えてくれる... らしいです.英語むずいな*1.Springもframeworkだよね? って一瞬思いましたが,ここでいうフレームワークはプレゼンテーションフレームワークなどを意識しているようです... たぶん.それで,ContextLoaderというwebなパッケージに入っているクラスなどを使うと,これまで愛用してきたXmlBeanFactoryプログラマチックに扱わなくてもいいとか.うーん,なんだかよく分かりません.
分からないときはソースを見れるのがオープンソースのいいところ.というわけでちょっとApplicationContextを見てみると,これはBeanFactoryextendsしたインタフェースなんですね.なぁんだ,それなら怖くないぞ!
ではそのApplicationContextBeanFactoryにどんな能力を付加してくれているのでしょうか? ドキュメントによると,

  • 国際化対応のメッセージにアクセスできます.
  • URLやファイルなどのリソースにアクセスできます.
  • イベントを伝播してくれます.
  • 複数のコンテキストをロードできます.

... 悪くはないし,真っ当なアプリケーションでは必要なのでしょうが,ちょっと魅力に乏しい感じ.
まぁ,いいでしょう.一つずつやっていきましょう.
ApplicationContextBeanFactoryでもあることが分かったので,まずはこれまで遊んできたサンプルをApplicationContext対応に変更してみることにします.
... むぅ.
このドキュメント,ApplicationContextを使ったコードがないなぁ.3章の中にはまったく出てこないぞ.自分で調べろってか? いいでしょう.そんなときはソースを見られるのがオープンソースのいいところ.おっ,FileSystemXmlApplicationContextなるクラスを発見! 名前は物々しいけれど,要するにXMLの定義ファイルを読み込んでくれるらしい.
ということで,まずは実行用クラスの

            XmlBeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml"));

            FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");

に変更します.コンストラクタの引数は文字列なので,自分でInputStreamにしなくてもいいんですね.ちょっとだけラクチン?
そして変数factorycontextに置換します.
おやぁ? BeanFactoryのカスタマイズのところで,postProcessBeanFactoryの呼び出しがエラーに? なるほど,postProcessBeanFactoryの引数は以前も書いたようにConfigurableListableBeanFactoryなのですが,ApplicationContextListableBeanFactoryHierarchicalBeanFactoryextendsしているだけなんですね.じゃあカスタマイズはどうするんだろう? ようやくBigDecimalを使えるようになったのに,どうするんだろう? そんなときはソースを見れるのが(以下略).
なるほどー,ApplicationContextは,初期化中に自分をカスタマイズしてくれるんですね.プログラマチックに扱わなくてもいいっていうのはそういうことかぁ.
ということで,カスタマイズ用(CustomEditorConfigurerPropertyPlaceholderConfigurer)の定義も一緒にApplicationContextに与えてあげればいいようです.FileSystemXmlApplicationContextのコンストラクタには,文字列の配列を受け取るものもあるので,これを使ってBean定義のXMLとカスタマイズ用のXMLを一度に食べさせたあげましょう.

            FileSystemXmlApplicationContext context = 
                new FileSystemXmlApplicationContext(
                    new String[] {"factory.xml", "beans.xml"});

これでBeanFactoryカスタマイズ用のコードがなくなってすっきり.
おっと,BeanFactoryを後片付けするためのdestroySingletons()もエラーになってやがる.そんなときはソースを(以下略).close()にすればいいようです.
ということで,全部まとめると次のようになりました.

package study;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Main {
    public static void main(String args) {
        try {
            FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext(
                  new String { "factory.xml", "beans.xml" });

            String[] names = context.getBeanDefinitionNames();
            for (int i = 0; i < names.length; ++i) {
                Object bean = context.getBean(names[i]);
                System.out.println(names[i] + " : " + bean);
                Map props = BeanUtils.describe(bean);
                for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
                    Map.Entry entry = (Map.Entry) it.next();
                    System.out.println("\t" + entry.getKey() + "=" + entry.getValue());
                }
            }

            context.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

たいして変わってないんですけどね.そうかぁ,BeanFactoryのカスタマイズは自分でコードを書く必要がなかったのかぁ.それを先に教えてくれよー.でもまぁ,日記のネタとしてはこれでよかったかな.水面下で行われていることもちょっとは分かった気がしますから.
これでApplicationContextの付加機能を使う準備が出来ました.次回はその付加機能を使うべく,「3.10 Added functionality of the ApplicationContext」を学習します.

*1:コードより文章が多いと挫折しそうになります