Spring Framework 入門記 Beanその4 depends-on,autowire,dependency-check

コンテナの動きを調整する<bean>要素の属性がいくつかあります.

  • depends-on属性

depends-on属性は,Beanの初期化順に依存関係がある場合に使用します.constructor-basedではあまり問題にならないと思いますが,setter-basedでBeanの参照を設定する場合には,初期化順の依存関係が問題になる場合が考えられます.例えばAとBという2つのBeanがあり,AはBへの参照をプロパティに持っているとします.つまり,AはsetB(B)というsetterを持つわけですが,このsetterの中で,Bのプロパティにアクセスしたり,メソッドを呼び出したりするかもしれません.ということは,AのsetB(B)が呼び出されたときには,Bのプロパティの設定は完了していないといけないわけです.このような場合,AはBに依存していることを,depends-on属性で明示します.

    

depends-onで指定する値は,<ref>要素のbean属性と同じく,Beanの名前です(「Spring Framework 入門記 Beanその1 id/name属性とsingleton属性」を参照).
Apache AvalonECM(Excalibur Compoenent Manager)ではコンポーネントの初期化順を制御できなかったので(たぶん),これはありがたいです.

  • autowire属性

setter-basedでもconstructor-basedでも,他のBeanへの参照を設定することができます.これまでは,参照するBeanを<ref>属性で明示的に指定していたわけですが,これをコンテナにお任せすることができます.これをautowiringと呼びます.このためにautowire属性を使用することができます.
autowire属性には,次の値を指定することができます.

no
デフォルトです.autowiringは行いません.
byName
Beanの名前でautowiringを行います.
byType
Beanの型でautowiringを行います.

byNameを指定すると,そのBeanのプロパティ名と一致する名前を持つBeanがあれば,それをプロパティに設定してくれます.
byTypeを指定すると,そのBeanのプロパティの型と一致するBeanが(一つだけ)あれば,それをプロパティに設定してくれます.ただし,プロパティの型と一致するBeanが複数あると,またしても例外が吹っ飛んできます.
一見便利そうなautowiringですが,個人的にはあまり魅力を感じません.ないよりはあってもいい,くらいの感じです.というのも,ある程度Beanの数が増えてくると,byNamebyTypeとも使えないことになると思うからです.byNameは,参照する側のBeanのインスタンスが複数あり,それぞれのプロパティに異なった値を設定する場合に使えなくなります.byTypeは,参照される側のBeanのインスタンスが複数ある場合に使えなくなります.ちょっとしたサンプル程度なら楽ができるとは思うのですが,その程度だったら明示的に指定してもたいした手間がかかるわけではないわけですから,結局大きなメリットにはならないと思うわけです.

  • dependency-check属性

属性名からはピンときませんが,この属性により,Beanのプロパティの設定漏れをコンテナにチェックしてもらうことができます.Dependency Injectionのdependencyということで全く正しいのだと思いますが,なんだかピンとこないんですよね.
dependency-check属性には,次の値を指定することができます.

none
プロパティの設定漏れをチェックしません.
simple
プリミティブ型およびjava.lang.String型のプロパティに設定漏れがないかチェックします.
objects
java.lang.String以外のオブジェクト(参照)型のプロパティに設定漏れがないかチェックします.
default
デフォルトです.noneと同様,設定漏れをチェックしません.

Spring Framework Reference Documentation」の「Checking for dependencies」ではobjectsではなくobjectとなっていますが,DTDではobjectsとなっています.また,simpleはプリミティブ型に加えてコレクション型のプロパティをチェックするとなっていますが,1.0RC2ではコレクション型はチェックされず,代わりに(?)java.lang.String型のプロパティがチェックされます.このあたりの説明は,DTD中のコメントの方が実装と一致しているようです.
dependency-checkもありがたいようで,実はそれほどでもない感じです.プロパティに値や参照を設定していないことをチェックするということは,それらのプロパティにデフォルト値がないために,明示的に設定することを強制したいということだと思います.それはよくある状況です.しかし,そのような状況はクラス単位で出てくる話だと思うのですね.ですが,dependency-check属性は<bean>要素の属性です.つまり,インスタンス単位で設定します.とあるインスタンスの設定(<bean>要素)で,dependency-check属性の指定が漏れてしまったら,全然意味がありません.このようなチェックが必要であれば,そのクラスのコンストラクタで強制するのが適当だと思います.


今回は,コンテナの動作を調整する<bean>要素の3つの属性について学習しました.
現時点ではdepends-on属性以外はあまり使う価値を感じませんが,経験を深めていくと考えが変わるかどうか... 将来のお楽しみということで.
次はライフサイクルにするかBigDecimalを扱うPropertyEditorを作るか.明日の気分で決めたいと思います.

Inter 2 - 0 Ancona

\(^o^)/ \(^o^)/ \(^o^)/ \(^o^)/ \(^o^)/
一ヶ月半ぶりの勝利です.しかもレコバの先制ゴール! もう最高!!
たとえ相手が未だ今シーズン未勝利で最下位のアンコナであったとしても,勝ちは勝ち.今欲しかったのはインテルの勝利だったのだぁ〜.
アドリアーノレコバの組み合わせ,結果出てるんだからもっと使って欲しいな.そうするとヴィエリをどうするってのが悩ましいことは確かなんですが.
って,何? レコバは中盤だったの? いいのだ,点取ってくれれば.いいのか?

Spring Framework 入門記 Beanその5 setter-basedとconstructor-basedの混在

ちょっと追加.
setter-basedとconstructor-basedは,一つのBean定義の中で混在することができます.
例のPersonを例として...

<?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">
        <constructor-arg index="0" type="java.lang.String"><value>koichik</value></constructor-arg>
        <constructor-arg index="1" type="java.net.URL"><value>http://d.hatena.ne.jp/koichik/</value></constructor-arg>
        <property name="friend"><ref bean="makotan"/></property>
    </bean>
</beans>

とすると,コンストラクタでnamediaryが設定され,その後setterメソッドを通じてfriendが設定されます.

sectionモジュール

なんだか日記が妙に長くて,自分が書いたネタを探すのに苦労してしまいます.
ということで,sectionモジュールというものを導入してみました.
どうにか見た目などを整えて,これはなかなかと思いきや,一部リンクがうまくいってないような?
どうやら,一度に複数の小見出しを作って更新すると,その小見出しは同じ名前(<a>要素のname属性)を持ってしまうようです.確かにそういうことを何度かやってしまったなぁ.
ということで,今後は見出し単位で更新するよう注意しよう.

お買い物スタイル

先週購入したYSLのパンツを取りに新宿へ.

  • 黒のジップアップブルゾン(DIOR HOMME 04SS)
  • 黒とグレイのボーダーニット(BURBERRY Prosum 03SS)
  • 黒のコーティング・デニム(JIL SANDER 03-04AW)
  • 黒のショートブーツ(JIL SANDER 03-04AW)

昨秋購入したJIL SANDERのコーティング・デニム,遅ればせながらお出かけデビュー.
先週購入したDIOR HOMMEのブルゾンは早速デビュー.なかなか相性よさげです.このブルゾンは,DIOR HOMMEにしてはシンプルというか,余計なものが付いていなくて,エディ本来のミニマルさが強く感じられて大ヒットです.

新宿伊勢丹のGUCCIにて

雑誌で見かけて取り寄せをお願いしていた携帯ストラップが入荷していたので購入しました.
GUCCIらしく,鈍く(黒っぽく?)輝く棒状のシルバー(7mm×75mm)がついた携帯ストラップ.すごくシンプルなのに存在感があって,まさにTOM FORD FOR GUCCI
しかしながら,自分の使っているプラスチッキーな携帯に合わせる気がしません.スタイリッシュな携帯といえばauINFOBARが話題になっていましたが,深澤さんのデザインしたものっておもちゃっぽい(安っぽいという意味ではなく,玩具っぽいっていうか)感じがあまり好きではないんですよね.かっこいい携帯ないかなぁ.いっそ,トム・フォードがデザインした携帯があれば... Tom Ford for DoCoMo by SONYみたいな.

Malaysian GP決勝 M.Schumacher 優勝

\(^o^)/
開幕戦と異なり接戦だったようですが,きっちり開幕から連続ポール・トゥ・ウィン,さすがです.
琢磨はこの週末ずっと波に乗れなかったようで,残念です.バーレーンで頑張ってほしいですね.

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のプロパティの型に変換して設定してくれます.どのような型がサポートされているかというと,コンテナが直接サポートしているらしきものは次の型です.

  1. intなどのプリミティブ型.
  2. 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:日記のネタとも言います

Spring Framework 入門記 Beanその3 constructorによるインスタンス生成

setter-basedでBeanのプロパティに値や参照を設定する方法が分かったので,次はconstructor-basedに進みます.
コンテナが扱うオブジェクトが全てJavaBeansの約束事に従う,すなわちデフォルトコンストラクタを持ち,プロパティを設定することで状態を変更できるのであれば,setter-basedだけでも十分なのかもしれません.しかし,世の中にはJavaBeansではないものも多々あります.そのようなものには,コンストラクタに引数を与えてインスタンスを生成しなければならないものもあります.典型的なものとして不変オブジェクトをあげることができますね.通常不変オブジェクトは,コンストラクタに渡された引数で初期化されると,それ以降は状態を変更することができません.ですから,setter-basedではうまく扱えないわけです.ということで,constructor-basedも必要になります.
constructor-basedも使い方は簡単で,<property>要素の代わりに<constructor-arg>要素を使って引数の値や参照を指定すればよいだけです.内容には<property>要素と同じように,<value>要素や<ref>要素,<null>要素を記述することができます*1
不変オブジェクトといえば,すぐに思いつくのはプリミティブ型のラッパーですね.ということで,やってみました.

<?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="number" class="java.lang.Integer">
        <constructor-arg><value>100</value></constructor-arg>
    </bean>
</beans>

これを前回作成した実行用クラスで実行してみると,

number : 100
    class=class java.lang.Integer

となりました.プリミティブ型でも使えますね.
ちなみに,

    <bean id="string" class="java.lang.String">
        <constructor-arg><value>Hoge Hoge</value></constructor-arg>
    </bean>

なんてやっても動きました.意味は全くありませんが.
Javaのコンストラクタには名前がなく*2,引数の数と型の並びでしか区別されません.このため,<constructor-arg>要素には順序と型を指定することができます.順序は,index属性で,型はtype属性で指定します.
ということでお試しコーナー.
setter-basedの学習で使ったPersonクラスに次の2つのコンストラクタを追加します.

    public Person(String name, String url) throws MalformedURLException {
        this.name = name;
        this.diary = new URL(url);
    }
    public Person(String name, URL diary) {
        this.name = name;
        this.diary = diary;
    }

そして,定義ファイルを以下のようにします.

<?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">
        <constructor-arg><value>koichik</value></constructor-arg>
        <constructor-arg><value>http://d.hatena.ne.jp/koichik/</value></constructor-arg>
    </bean>
</beans>

ここではまだ,type属性を指定していないため,どちらのコンストラクタが呼び出されるかは明示的ではありません.どうなるのか興味津々で実行してみると...

org.springframework.beans.FatalBeanException: 
    Could not instantiate class [study.Person]; constructor threw exception; 
        nested exception is java.net.MalformedURLException: no protocol: koichik

ありゃりゃ? なぜか最初のコンストラクタの第2引数(diary)に最初の<constructor-arg>要素の値である"koichik"が渡されてしまったようです.なぜ?
気を取り直して,まずはindex属性を指定します.

        <constructor-arg index="0"><value>koichik</value></constructor-arg>
        <constructor-arg index="1"><value>http://d.hatena.ne.jp/koichik/</value></constructor-arg>

これはうまく動きましたが,呼び出されたコンストラクタは,Person(String, URL)でした.なぜだろう? やはり曖昧なのはよくないということでしょうか.
次に,type属性に変更します.

        <constructor-arg type="java.lang.String"><value>koichik</value></constructor-arg>
        <constructor-arg type="java.net.URL"><value>http://d.hatena.ne.jp/koichik/</value></constructor-arg>

これは当然Person(String, URL)が呼び出されます.しかし,Person(String, URL)Person(URL, String)がある場合には,どちらが呼び出されるか曖昧になってしまいます.このような場合には,index属性とtype属性の両方を指定すべきでしょう.
なお,type属性には完全限定名を指定する必要があります.油断してtype="String"などと記述すると例外がぶっ飛んでくるので気をつけましょう.
次は,コンテナの動きを調整するパラメータについて学習します.

*1:DTD的には他にもいくつかあるらしいのですが,それはまた今後ということで...

*2:クラス名と同じ名前を持ちますが,多重定義されたコンストラクタ各を区別するユニークな名前はありません.