Spring Framework 入門記 Beanその2 setterによるプロパティの設定
「3.3. Properties, collaborators, autowiring and dependency checking」を読みながら,プロパティの設定について学習します.
Spring FrameworkでBeanのプロパティを設定するには,setter-basedとconstructor-basedがあります.setter-basedでは,Beanのプロパティをsetterメソッドを通じてコンテナに設定してもらいます.constructor-basedでは,Beanのプロパティをコンストラクタを通じてコンテナに設定してもらいます.
まずは,setter-basedから始めます.
setterメソッドを通じてBeanのプロパティを設定するには,<bean>
の子要素として<property>
要素を記述します.
<bean name="foo" class="study.Foo"> <property name="bar">〜</property> </bean>
<property>
要素の内容にはいろいろ記述できるようですが,まずは<value>
要素による値の設定について.
<value>
要素を記述すると,コンテナはその内容文字列をBeanのプロパティに設定してくれます.
<property name="text"><value>Spring Framework</value></property>
「はじめてのSpring Framework」では,value
要素が冗長だなぁ,と書いたのですが,「3.3. Properties, collaborators, autowiring and dependency checking」の中に<value>
要素を記述しないで<property>
要素の内容に直接,値を記述している例がありました.なーんだ,省略できるんじゃん,と思ったのですが,実際にやってみたらバリデーションではねられました.(;_;)
DTD的には省略不可になっているので,単なるドキュメントの間違いのようです.まぁ,そういうもんだってことであきらめましょう.
コンテナは,<value>
要素の内容文字列をBeanのプロパティの型に変換して設定してくれます.どのような型がサポートされているかというと,コンテナが直接サポートしているらしきものは次の型です.
int
などのプリミティブ型.java.lang.String
型.
ずいぶんあっさりしています.それ以外の型は,java.beans.PropertyEditor
を利用するのだそうです.ふーん.JavaBeansなんだからそれが真っ当といえばそうなのかなぁ.しかし,GUIコンポーネント以外では無縁な代物だと思いこんでいました,PropertyEditor
.
なお,次のPropertyEditor
実装クラスが標準でコンテナに組み込まれています(「4.3.2 Built-in PropertyEditors
, Converting types」より).
ClassEditor
- 内容文字列を完全限定名とする
java.lang.Class
オブジェクトにしてくれます. FileEditor
- 内容文字列をパスとする
java.io.File
オブジェクトにしてくれます. LocaleEditor
- 内容文字列を
ja_JP
のような言語コード等とするjava.util.Locale
オブジェクトにしてくれます. PropertiesEditor
- 内容文字列を読み込んだ
java.util.Propertie
オブジェクトにしてくれます. StringArrayPropertyEditor
- 内容文字列をコンマで区切った
java.lang.String
の配列にしてくれます. URLEditor
- 内容文字列をURLとする
java.net.URL
オブジェクトにしてくれます.
あれー,java.math.BigDecimal
はサポートしてくれてないのですね.それは悲しい.と一瞬思いましたが,学習テーマ*1として好都合なので,近日中に挑戦します.
ここまでの分を簡単に確認してみることにします.まずはBeanを作ります.
package study; import java.net.URL; public class Person { private String name; private URL diary; //以下,上記フィールドのsetter/getterが続く }
そして定義ファイルを用意します.
<?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="koichik" class="study.Person"> <property name="name"><value>Koichi Kobayashi</value></property> <property name="diary"><value>http://d.hatena.ne.jp/koichik/</value></property> </bean> </beans>
実行用のクラスを作って
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.xml.XmlBeanFactory; public class Main { public static void main(String args) throws Exception { XmlBeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml")); 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()); } } } }
実行すると,
koichik : study.Person@9e5c73 diary=http://d.hatena.ne.jp/koichik/ class=class study.Person name=Koichi Kobayashi
となり,URL
型を含むプロパティの値が設定できていることが確認できました((class
"プロパティ"が出力されているのはご愛敬ということで.ちょっと便利そうだしネ.)).
次は,他のBeanへの参照を設定する<ref>
要素です.これがIoC/Dependency Injectionの名前の由来になっている機能ですよね,きっと.
使い方は,<ref>
要素のbean
属性で参照するBeanの名前を指定するだけです.この名前は,前回いろいろ試した<bean>
要素のid
属性またはname
属性で指定されたものです.
ということで,先のPerson
クラスにstudy.Person
型のfriend
というプロパティを追加して,定義ファイルを次のように変えてみます(makotan,ごめん).
<?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="koichik" class="study.Person"> <property name="name"><value>Koichi Kobayashi</value></property> <property name="diary"><value>http://d.hatena.ne.jp/koichik/</value></property> <property name="friend"><ref bean="makotan"/></property> </bean> <bean id="makotan" class="study.Person"> <property name="name"><value>makotan</value></property> <property name="diary"><value>http://d.hatena.ne.jp/makotan/</value></property> <property name="friend"><ref bean="koichik"/></property> </bean> </beans>
これを実行すると,
koichik : study.Person@9e5c73 diary=http://d.hatena.ne.jp/koichik/ class=class study.Person friend=study.Person@b25b9d name=Koichi Kobayashi makotan : study.Person@b25b9d diary=http://d.hatena.ne.jp/makotan/ class=class study.Person friend=study.Person@9e5c73 name=makotan
となり,無事に関連付けができたことを確認できました.相互参照していても問題なしですね.
なお,<ref>
要素でbean
属性の代わりにlocal
属性を指定すると,Beanのid
だけで参照するようになります(name
属性は参照されません).この場合,local
属性が参照するid
を持つBeanが定義ファイル中に記述されていないと,XMLのバリデーションでエラーになります.local
属性はIDREF型なのですね.XMLエディタを使う場合には,編集時にチェックできるのでよさげです.
なぜlocal
という名前なのかと思いきや,Springのコンテナは階層化することができるらしく,bean
属性で指定した場合は,現在のコンテナに該当のBeanが見つからなければ,親のコンテナから探してくれるとのことです.それがlocal
属性の場合には,一つのXMLファイル(すなわち一つのコンテナ)中にあるBeanを参照するだけですから,local
という名前なんですね(おそらく).コンテナの階層化についても近々学習せねば.
それから,プロパティの値にnull
を設定するには,<null>
要素を使用します.
<property name="name"><null/></property>
次のように書くと,null
ではなく,空文字列になってしまいます.
<property name="name"><value></value></property>
プロパティの型が空文字列から変換できる値を持たない場合(int
など)の場合,例外がビッシビシ(死語)飛んでくるので注意しましょう.
次は,constructor-basedを学習します.
*1:日記のネタとも言います