Spring Framework 入門記 Beanその8 BeanFactoryのカスタマイズ
IoC − 制御の逆転.ということで,ここまでは一方的にコンテナ側から呼び出されることばかり見てきましたが,世の中やっぱり逆転できないこともあります(たぶん).そんな時に備えて,Beanの側からコンテナを利用することもできます.
コンテナを利用するには,コンテナを手に入れなくてはなりません.そんな場合は,BeanFactoryAware
インタフェースをimplements
します.すると,そのメソッドsetBeanFactory(BeanFactory)
を通じてコンテナを入手することが出来ます.この部分はDependency Injectionなのですね.
Bean定義XMLの中で,自分がどんな名前で定義されたのかを知りたい場面もあるかもしれません(本当か?).そんな場合は,BeanNameAware
インタフェースをimplements
します.すると,そのメソッドsetBeanName(String)
メソッドを通じて自分の名前を入手することが出来ます.なぜ必要なんだろう?
ま,自分の名前はおまけ程度ということで忘れてもよさげな気がします.よし,しばし忘れましょう.
次はFactoryBean
のことが紹介されていたりするのですが,ちょっと毛色が違う気がするのでこれもしばし忘れましょう.
さて,コンテナを手に入れるとどんな素敵なことが出来るのでしょうか?
getBean(String)
getBean(String, Class)
isSingleton(String)
getAliases(String)
containsBean(String)
... 素敵ですか?
ま,コンテナもおまけ程度ということで忘れてもよさげな気がします.よし,しばし忘れましょう.
次は,「3.6 Customizing the BeanFactory」.って,BeanFactory
忘れちゃダメじゃん.
BeanFactory
をカスタマイズするには,BeanFactoryPostProcessor
というインタフェースをimplements
します.このインタフェースは,postProcessBeanFactory(ConfigurableListableBeanFactory)
というメソッドを持っています.つまり,実際にカスタマイズできるのはBeanFactory
をextends
したインタフェースであるConfigurableListableBeanFactory
というインタフェースをimplements
したコンテナなんですね.ほとんどの場合,XmlBeanFactory
しか使わないと思いますが,このクラスは当然ConfigurableListableBeanFactory
をimplements
しています.
さて,BeanFactoryPostProcessor
を使うとどんなことができるのでしょうか.Springに用意されているBeanFactoryPostProcessor
実装クラスには,次のものがあるようです.
CustomEditorConfigurer
PropertyEditor
を組み込みます.PropertyOverrideConfigurer
- プロパティリストの記述に従い,Beanのプロパティ値をオーバーライドします.
PropertyPlaceholderConfigurer
- 定義ファイル中に
${var}
のように記述された(一種の)変数をプロパティリストの記述に従い置換します. PreferencesPlaceholderConfigurer
PropertyPlaceholderConfigurer
と同様ですが,プロパティリストではなくJ2SE1.4のプリファレンスAPIを使用します.
うん,こっちのほうが素敵(キラッ).あるじゃないですか,CustomEditorConfigurer
.これですよ,探していたのは.
というわけで,いつものように実験ですが,せっかく見つかったCustomEditorConfigurer
は先延ばしにして,ここではPropertyPlaceholderConfigurer
で遊んでみます.
まずは定義ファイルに変数を埋め込んで...
<bean name="foo" class="study.Foo"> <property name="text"><value>${foo}</value></property> </bean>
実行用のクラスでBeanFactory
にPropertyPlaceholderConfigurer
を組み込みます.
package study; import java.io.FileInputStream; import java.util.Iterator; import java.util.Map; import java.util.Properties; import org.apache.commons.beanutils.BeanUtils; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; 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")); Properties params = new Properties(); params.put("foo", "FOO"); PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); configurer.setProperties(params); configurer.postProcessBeanFactory(factory); String names = factory.getBeanDefinitionNames(); for (int i = 0; i < names.length; ++i) { Object bean = factory.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()); } } factory.destroySingletons(); } catch (Exception e) { e.printStackTrace(); } } }
実行します.
- Loading XML bean definitions from (no description) - Creating shared instance of singleton bean 'foo' initialized. foo : study.Foo@11671b2 text=FOO class=class study.Foo - Destroying singletons in factory {org.springframework.beans.factory.xml.XmlBeanFactory defining beans [foo]; Root of BeanFactory hierarchy} destroyed.
Fooオブジェクトのtextプロパティが確かに置き換わっています.一応は成功です.
しかし... これが素敵とは言い難いですね.プロパティリストの内容はファイルとして外に出せばよいとしても,いろいろなBeanFactoryPostProcessor
の組み込みをいちいちプログラミングしたくもありません.よく見ると,PropertyPlaceholderConfigurer
もBeanっぽく使えそうな感じがします.ということで,こいつを定義ファイル化しましょう.
といっても,通常のBean定義とは別にしておいた方が使いまわしが楽そうな気がします.ということで,BeanFactory
のカスタマイズ用定義ファイル作成!
<?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="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > <property name="properties"> <props> <prop key="foo">FOO</prop> </props> </property> </bean> </beans>
初登場の<props>
を使ってしまいました.「3.8.3 Bean definitions specified in XML (XmlBeanFactory)」を先取りです.
次にこれを読み込んで,BeanFactory
をカスタマイズするわけですが,このカスタマイズ用定義ファイルを読み込むBeanFactory
とこれまでのBean定義ファイルを読み込むBeanFactory
の関係をどうしたものか? このあたりの学習はまだなので,とりあえず独立させておきます(手抜きモード).
package study; import java.io.FileInputStream; import java.util.Iterator; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 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")); //BeanFactoryのカスタマイズ XmlBeanFactory configurerFactory = new XmlBeanFactory(new FileInputStream("factory.xml")); String configurers = configurerFactory.getBeanDefinitionNames(); for (int i = 0; i < configurers.length; ++i) { Object bean = configurerFactory.getBean(configurers[i]); if (bean instanceof BeanFactoryPostProcessor) { //postProcessするのはbeans.xmlを読み込んだBeanFactory *1; } } factory.destroySingletons(); } catch (Exception e) { e.printStackTrace(); } } }
これで実行!!
- Loading XML bean definitions from (no description) - Loading XML bean definitions from (no description) - Creating shared instance of singleton bean 'propertyPlaceholderConfigurer' - Creating shared instance of singleton bean 'foo' initialized. foo : study.Foo@18b81e3 text=FOO class=class study.Foo - Destroying singletons in factory {org.springframework.beans.factory.xml.XmlBeanFactory defining beans [foo]; Root of BeanFactory hierarchy} destroyed.
大・成・功!
これで,新たにBeanFactoryPostProcessor
実装クラスをコンテナに組み込みたくなった場合でも,定義ファイルに書き加えてやるだけで済みます.とはいえ,既存のBeanFactoryPostProcessor
実装クラスを使ったというだけで,自分で何か作ったわけではないのがいまひとつ.
ということで,次はBeanFactoryPostProcessor
をimplements
したクラスを作ろうかなぁ.まぁ,気が向けばということで.
*1:BeanFactoryPostProcessor) bean).postProcessBeanFactory(factory); } } //Beanの表示 String[] names = factory.getBeanDefinitionNames(); for (int i = 0; i < names.length; ++i) { Object bean = factory.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(