Spring Framework 入門記 BeanWrapper

今回は「Chapter 4. PropertyEditors, data binding, validation and the BeanWrapper」です.3章と比べると分量が少ないので,あっという間に終われそうです.というかですね,このタイトルから分かるとおり,この章は3章からこぼれたと思われる4つのネタを解説しているだけという感じがするんですね.しかも,そのうちのPropertyEditorはすでに「Beanその9 PropertyEditorだよ」で学習済み,そしてdata bindingはというと,

4.2. Binding data using the DataBinder
The DataBinder builds on top of the BeanWrapper.

これだけですよ,これだけ.(^^; validationにいたっては,導入部でそういうものがあると紹介されている程度.
どうしろっていうんでしょうねぇ? いやその,ソースを見れるのがオープンソースのいいところではあるのですが,あうあう.
というわけで(謎),残りはBeanWrapperだけとなりました.えっ? それでいいのかって? いいんです!(死語).
そんなわけでBeanWrapperですが,これはBeanのプロパティに式言語(EL)のような文字列でアクセスするユーティリティ的なものですね.ドキュメントによると,

name
Beanのnameというプロパティにアクセスします.getName()setName()に相当します.
account.name
Beanのaccountというプロパテが参照するBeanのnameというプロパティにアクセスします.getAccount().getName()などに相当します.
account[2]
Beanのaccountという配列またはListなどのプロパティの3番目の要素にアクセスします.getAccount()[2]またはgetAccount().get(2)などに相当します.

ということで,うまく使えればいい感じ.
使い方も簡単で,操作対象のBeanをBeanWrapperImplでラップして,getPropertyValue(String)またはsetPropertyValue(String, Object)などのメソッドを呼べばいいだけのようです.
さて,これをどう使いましょうか.どうせなら,上記の式言語もどきをBean定義XMLの中で使えればいいですね.XML中に定義されたBeanのプロパティをたどった結果を好きなBeanのプロパティに設定できたりとか.うんうん.
そんな場合に使えるのがFactoryBeanです.BeanFactoryじゃありませんよ,「Beanその10 FactoryBean」で学習した,それ自身がファクトリであるBeanというヤツです.そう,こいつは自分もBeanとして定義ファイルに記述されるものの,コンテナが扱うのはこいつが返したオブジェクトでした.そうです,そこで式言語もどきを評価した結果を返してあげれば... うん,その方が素敵(キラッ).
よし,早速作ってみましょう.
式言語もどきとそれを適用する対象(ターゲット)のオブジェクトをプロパティにすればいいはずです.その他FactoryBeanに必要なものを含めて,次のようになりました.

package study;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.FactoryBean;

public class PropertyValueFactoryBean implements FactoryBean {
    private Class objectType;
    private boolean singleton;
    private Object target;
    private String expression;
    public Object getObject() {
        BeanWrapper wrapper = new BeanWrapperImpl(target);
        return wrapper.getPropertyValue(expression);
    }
    //以下はフィールドのgetterおよびsetter
    public Class getObjectType() {
        return objectType;
    }
    public void setObjectType(Class objectType) {
        this.objectType = objectType;
    }
    public boolean isSingleton() {
        return singleton;
    }
    public void setSingleton(boolean singleton) {
        this.singleton = singleton;
    }
    public Object getTarget() {
        return target;
    }
    public void setTarget(Object target) {
        this.target = target;
    }
    public String getExpression() {
        return expression;
    }
    public void setExpression(String expression) {
        this.expression = expression;
    }
}

ちょっとプロパティの数が多いためコードが長く見えますが,中身は無いも同然ですね.
さて,実験用にいくつかしょぼいクラスを作ります.

package study;

public class ObjectHolder {
    private Object object;
    public Object getObject() {
        return object;
    }
    public void setObject(Object object) {
        this.object = object;
    }
}
package study;
import java.util.Collection;

public class CollectionHolder {
    private Collection collection;
    public Collection getCollection() {
        return collection;
    }
    public void setCollection(Collection collection) {
        this.collection = collection;
    }
}
package study;

public class StringHolder {
    private String string;
    public String getString() {
        return string;
    }
    public void setString(String string) {
        this.string = string;
    }
}

それぞれ,ObjectCollectionStringのプロパティを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 name="root" class="study.ObjectHolder">
        <property name="object">
            <bean class="study.CollectionHolder">
                <property name="collection">
                    <list>
                        <bean class="study.StringHolder">
                            <property name="string"><value>Spring is comming.</value></property>
                        </bean>
                        <bean class="study.StringHolder">
                            <property name="string"><value>Spring is here.</value></property>
                        </bean>
                    </list>
                </property>
            </bean>
        </property>
    </bean>
    <bean name="foo" class="study.StringHolder">
        <property name="string">
            <bean class="study.PropertyValueFactoryBean">
                <property name="objectType"><value>java.lang.String</value></property>
                <property name="target"><ref bean="root"/></property>
                <property name="expression"><value>object.collection[1].string</value></property>
            </bean>
        </property>
    </bean>
</beans>

前半で定義しているrootというBean以下は,式言語もどきでアクセスする対象となるBeanのツリーです.
後半で定義しているfooでは,そのプロパティの値を設定するために,PropertyValueFactoryBeanを使用しています.ターゲットはrootです.
で,これを「Contextその1 ApplicationContext」のMainクラスで実行すると...

foo : study.StringHolder@787d6a
    string=Spring is here.
    class=class study.StringHolder

という結果になりました.ちゃんとexpressionプロパティの値を評価した結果が入っています.やったね.
これがそんなに便利という感じはしませんが,困ったときには使えるかもしれません.どう困ったときに使えるかは分かりませんが.
さぁ,これで4章も終わりにしてしまおう.いよいよ次は「Chapter 5. Spring AOP: Aspect Oriented Programming with Spring」に進みます.楽しみ(^o^)