Spring Framework 入門記 Contextその5 メソッド呼び出し
やばいです.3章を勢いで終わらせないと,モチベーションが低下してしまいます.刺激不足です.早く5章に達しなくては!幸い,3章の残りはすでに先取りしてしまったものも含まれていて,分量は多くありません.ということで,少し駆け足でいきましょう.
「3.11. Extra marker interfaces and lifecycle features」では,ContextパッケージはBeansパッケージ上に構築されているため,BeansFactory
同様,InitializingBean
インタフェース/<init-method>
要素,DisposableBean
インタフェース/<destroy-method>
要素を使うことができるということが説明されています.これらは「Beanその6」および「Beanその7」で学習しました.また,ApplicationContextAware
を使ってApplicationContext
を取得ことができます.これは「Contextその2」で学習しました.
「3.12. Customization of the ApplicationContext」では,BeanFactoryPostProcessor
を使用してApplicationContext
をカスタマイズすることが説明されています.これは,「Bean その8」および「Contextその1」で学習しました.
「3.13. Registering additional custom editors」では,カスタムエディタの組み込みが説明されています.これは「Bean その9」および「Contextその1」で学習しました.
ということで,今回は一気に「3.14. Setting a bean property as the result of a method invocation」へと進みます.メソッド呼び出しの結果をプロパティに設定する.うん,初めてですね.
ここまでの学習で,SpringのコンテナはあるBeanのプロパティの値(や参照)を,別のBeanのプロパティに設定してくれたりコンストラクタに引数として渡してくれることが分かりました.とってもありがたいことですが,やはり世の中それだけでは十分ではないこともあるでしょう.場合によっては,あるBeanのメソッドを呼び出したその結果をこっちのBeanのプロパティに設定したいんだ!ってこともきっとあるはず.ご安心ください,そんなあなたのためにMethodInvokingFactoryBean
をご用意させていただきました(またしても当然ですが,用意したのは私ではありません.).
MethodInvokingFactoryBean
は,「Beanその10」で学習したBeanFactory
をimplements
したもので,次のプロパティを持っています.
targetClass
static
なメソッドを呼び出す場合にそのクラス名(完全限定名)を設定するString
型のプロパティです.targetObject
- 非
static
な(インスタンス)メソッドを呼び出す場合にそのインスタンスを設定するObject
型のプロパティです. targetMethod
- 呼び出すメソッド名を設定する
String
型のプロパティです. staticMethod
static
なメソッドを呼び出す場合に,targetClass
とtargetMethod
の代わりに"java.lang.System.currentTimeMillis()"
のように一度に設定できるString
型のプロパティです.arguments
- 呼び出すメソッドの引数を設定する
Object
型の配列のプロパティです.なお,ドキュメントではargs
となっていますが誤りです. singleton
- メソッド呼び出しの結果オブジェクトがsingletonかどうかを設定する
boolean
型のプロパティです.このプロパティがtrue
の場合(デフォルト),メソッドの呼び出しは最初の1回だけになります. object
- メソッドを呼び出した結果の値(または参照)を保持するプロパティです.
singleton
がfalse
の場合,このプロパティが参照されるたびにメソッドが呼び出されます.
簡単そうですね.早速使ってみましょう.
おなじみの超単純なBeanを一つ用意.
package study; public class Foo { private String text; public String getText() { return text; } public void setText(String text) { this.text = text; } }
そしてBean定義XMLを作成します.
<bean id="version" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod"><value>java.lang.System.getProperty</value></property> <property name="arguments"> <list> <value>java.version</value> </list> </property> </bean> <bean id="foo" class="study.Foo"> <property name="text"><ref bean="version"/></property> </bean>
ここでは,システムプロパティの値をfoo
のtext
プロパティに設定しています.
これをいつものやつ(「Context その1」に出てくるMain
クラス)で実行すると...
version : 1.4.2_03 class=class java.lang.String bytes=49 foo : study.Foo@95cfbe text=1.4.2_03 class=class study.Foo
となり,text
プロパティにシステムプロパティの値が設定されていることが確認できました.
MethodInvokingFactoryBean
は任意のメソッドを呼び出せるので,あるクラスの初期化メソッドを呼び出すことにも使えるのではないかと一瞬思ったのですが,ちょっとうまくないようです.というのも,MethodInvokingFactoryBean
がメソッドを呼び出してくれるのは,object
プロパティが参照された場合だけなんですね.ということは,そのプロパティの値をどこかに設定しないと呼び出せないということ.じゃあ,void
なメソッドだとどうする? という疑問も.
ご安心ください,そんなあなたのために,InitializeMethodInvokingBean
をご用意させていただきました.今度はなんとこの私がご用意をさせていただきました.えっへん.
といっても全然大したことはなくて,MethodInvokingFactoryBean
を継承してみただけなんですが.
こんな感じ.
package study; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; public class InitializeMethodInvokingBean extends MethodInvokingFactoryBean { public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException { super.afterPropertiesSet(); try { invoke(); } catch (Exception e) { throw new RuntimeException("method invocation failed.", e); } } }
例外処理は思い切り手抜きしていますが気にしない,気にしない.
そして初期化メソッドを持つクラスを用意.
package study; public class Foo { private String text; public void initialize(String text) { this.text = text; } public String getText() { return text; } }
そしてBean定義のXMLファイルを用意.
<bean id="initializer" class="study.InitializeMethodInvokingBean"> <property name="targetObject"><ref bean="foo"/></property> <property name="targetMethod"><value>initialize</value></property> <property name="arguments"> <list> <value>Spring is here.</value> </list> </property> </bean> <bean id="foo" class="study.Foo"> </bean>
ここでは,initializer
というBeanは,foo
のinitialize
というメソッドを呼び出すように設定しています.
そして実行!
initializer : org.springframework.util.MethodInvoker$VoidType@1950198 class=class org.springframework.util.MethodInvoker$VoidType foo : study.Foo@da6bf4 text=Spring is here. class=class study.Foo
ちゃんと初期化されてますね.initializer
の表示がホゲホゲなのはご愛敬ということで.FactoryBean
は表示しても意味がないのです.間違いない.
でもこれ,あまり実用的とは言えなくて,かなりいかさまなんですよね.というのも,初期化メソッドの呼び出しはafterPropertiesSet()
でやっているのですが,これが呼び出されるのはこの初期化メソッド呼び出しBean自身の初期化の時一回きりなんです.ですから,対象のBeanがsingletonでなければ役に立たないのです.無念だ.心より恥じる.
ということで,これまでに学習してきたプロパティの設定,コンストラクタの呼び出しに加えて,メソッド呼び出しも使えることが分かりました.工夫次第でいろいろな使い他ができそうですね.