2.ApplicationContext.xmlファイルの記述方法
概要
Spring の設定ファイルについてみていきます。
まずbeanの取得方法ですが、以下のとおりでgetBean()メソッドを呼ぶだけです。ApplicationContext.xml設定ファイルの記述の内容が何であれ変わりません。
<beanの取得方法>
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("c:/test/context.xml");
DataSource ds = (DataSource)context.getBean("dataSource");
XMLで記述したbeanは、getBean()で簡単に取得できます。
どのbeanであれ取得方法は変わりませんが、XMLの記述を変えることで生成されるbeanを変更することができます。
これにより開発の後でも既存のプログラムを一切変更することなく、設定の変更のみでアプリケーションの動作を変更できます。
動作の変更を行うには、設定XMLファイルの記述方法を知っておく必要があります。
この記事では記述方法のみを見ていきます。
Springによるメリットは、Dependency Injectionについての記事にも記載していますのでお読みいただければと思います。
基本(beanタグ設定)
オブジェクト設定のサンプル
オブジェクトの設定は、Springの一番基本の設定です。
Springでは、xml ファイルにオブジェクトを定義し、ApplicationContextクラスを介してオブジェクトを受け取ります。
SpringのApplicaiotnContextはDIコンテナなどと呼ばれたりもします。
このコンテナは、オブジェクトの生成から破棄までを管理します。
では、早速XMLの記述方法のサンプルを見てみます。
【設定ファイルのサンプル】
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost/sample?useUnicode=true&characterEncoding=utf-8" />
<property name="maxActive" value="10" />
<property name="username" value="XXX" />
<property name="password" value="XXX" />
</bean>
<bean id="memberDao" class="my.MemberDao" >
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
<beanタグ、propertyタグについて>
基本は、beanタグで設定します。
beanタグは属性をいくつか持っています。destroy-method属性は、DIコンテナが終了するときに実行されるメソッドで、記述されたメソッド(上記ではclose())が実行されます。
beanタグは、propertyタグを持つことができます。
俗に言うJavaクラスのsetterに当たるもので、上記は例えば以下のような動作と全く同じになります。
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost/sample?useUnicode=true&characterEncoding=utf-8");
dataSource.setMaxActive(10);
:
引数の型が違うことにも注目してください。
上2つが文字列で、3つ目は数字です。Springの設定ファイルには文字列、数字の違いが表れていません。3つとも""で括られているだけです。
では、どうやって文字列に変換する、数字に変換する、といった判断をしているのでしょうか?
それは、Springが指定のクラスのsetterの型を見て、自動で変換しているのです。
ですのでSpringが知らない型があると当然変換できません。自作の変換クラスを作成して設定することもできますが、通常はサンプルのmemberDaoのようにします。
ref という属性が使用されています。
ref はDIコンテナ内に設定された他のbeanを参照するためのものです。(もちろん参照先のdataSourceというbeanはXMLに記述しなければなりません)
これでほとんどのクラス間の関連を記述できるかと思います。
<beanタグのidについて>
idは、Spring設定ファイル内で一意でなくてはなりません。(以下のオーバーライドの項も参照)
また、使用できる文字は、英数字、ピリオド、スラッシュなど、多岐に渡ります。
試してはいませんが、日本語も使えるかもしれません。(もちろん、日本語は使用しない方がよいですが)
beanの生成パターン(singleton, prototypeなど)
<bean id="member" class="my.Member" scope="prototype" />
scope属性は、生成の方法を指定します。
XMLの記述を変更することで、GoFのデザインパターンでいうシングルトン、プロトタイプパターンを実現でき、
開発の後でもXMLの記述だけで生成の方法を変更できます。
【scopeで設定できる内容と動作】
beanの生成タイミング(lazy-init)
<bean id="member" class="my.Member" lazy-init="true" />
beanには、lazy-initという属性があります。
これは、beanをいつ生成するかを指定するものです。
以下に設定できる値と動作を示します。
【lazy-initで設定できる内容と動作】
bean設定の引継ぎ(abstract)
実は、beanのプロパティなどを上書きすることができます。
ですので、同じような設定をする場合、親beanを指定すれば、記述の省略ができます。
【引継ぎサンプル】
<!-- 引継ぎ元(親) -->
<bean id="memAbstarct" class="my.MemberDao" abstract="true" >
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 単純に引き継いだ例。dataSourceが引き継がれています -->
<bean id="memberDao1" parent="memAbstarct" >
<property name="switch" value="1"/>
</bean>
<bean id="memberDao2" parent="memAbstarct" >
<property name="switch" value="3"/>
</bean>
<!-- propertyを引き継ぐだけなので親と違うクラスに対しても使用できます。 -->
<bean id="addressDao" class="my.addressDao" parent="memAbstarct" >
<property name="type" value="build."/>
</bean>
引継ぎのメリットとしては、記述を省略できることがすぐ思いつくかと思います。
しかし、それだけではありません。
機能拡張や変更時に、一箇所を修正することで全てが変更できるメリットもあります。
<補足>
プロパティにListやMapなどを持つ場合、親の値とマージすることもできます。
その場合は、merge属性を使用します。(merge="true" と記述します)
beanの設定のオーバーライド(上書き)
1つのContextファイル内はidが一意でなければエラーになります。
実はSpringは、複数のファイルをまとめてロードすることができます。
このとき違うファイルであれば同じidでも設定可能で、後で読み込まれたファイルの同じ id のbeanで上書きされます。
以下に複数のContextファイルを1つのコンテナに読み込むサンプルを示します。
【importタグを使用するサンプル】
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
">
<import resource="classpath:my/spring/base-context.xml"/>
</beans>
XML内から他のファイルをimportすることができます。
importで指定したファイル内に同じidが存在すれば、importしたファイルのbeanが上書きされます。
【ApplicationContextクラスのコンストラクタで指定するサンプル】
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
"classpath:/my/spring/base-context.xml",
"c:/test/context.xml"
);
FileSystemXmlApplicationContext クラスは、複数あるApplicationContextの1つです。
どのクラスも上記のように、ファイルを複数指定できるように設計されています。
上記のようにすれば同じidがあれば、クラスパス内の/my/spring/base-context.xml ファイルをc:/test/context.xmlで上書きすることになります。
<補足>
指定したファイルパスの"classpath:"という接頭子は、Springが用意している接頭子で、クラスパス内(すなわちjarファイル内)を指定のパスで
ファイルを検索します。
上書きの機能は、様々な機能で読み込んでいる共通のContextファイルがあるような場合にメリットがあります。
このような場合では、共通のContextファイルを変更してしまうと他の機能にも影響が出る可能性があります。
しかし、ある機能だけ共通Contextファイルの定義の一部を変更したい場合が必ず出てきます。そのような場合に大変便利です。
コンストラクタの指定(constructor-arg)
上記ではsetterによるbeanの設定を見ました。
ここではコンストラクタを指定してbeanの設定をする方法を見ます。
引数の数と型で指定する方法
<bean class="com.my.MyClass">
<constructor-arg index="0" value="あいう" />
<constructor-arg index="1" value="12" />
</bean>
コンストラクタを指定する場合は、constructor-argタグを使用します。
index属性に何番目の引数かを指定し、value属性にコンストラクタの引数の値を指定します。
さて、コンストラクタは複数存在しえます。どのコンストラクタを使うかをSpringに伝えなければいけません。
どうやって伝えるかというと、引数の数と型です。
上記は数だけを指定していますが、type属性で型を指定することもできますので、引数の数が同じ場合でも大丈夫です。
引数の名前で指定する方法
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
【ApplicationContextファイル】
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateanswer" value="42"/>
</bean>
引数の名前を指定する場合はname属性を使用します。
しかし、使用するためにはコードにアノテーションを記述する必要があります。
コンパイルされたバイナリには引数までは記述されないからです。
もう一つの方法として、デバッグフラグをつけてコンパイルする方法があります。
こうするとバイナリにも引数の情報が渡るので、Springも引数の名前が分かります。
しかし、コンパイル後のバイナリのファイル容量が大きくなるようなので、あまりやらない方がよいかと思います。
Factory(オブジェクトを生成するオブジェクトの設定)
Springで用意されている機能は、単純にbeanを生成するだけではありません。
自分自身を生成するのではなく、他のbeanを生成する定義をすることもできます。
その設定を見て行きます。
既存クラスのメソッドを利用する方法
ここでは、Calendarオブジェクトを生成することを考えてみます。
Calendarは abstractクラス なので通常newできません。 代わりにgetInstance()メソッドを呼び出すことになります。
上記のbeanの記述方法では生成不可能です。
しかし、下記のようにFactoryとして記述すると可能になります。
<bean id="dateFactroy" class="java.util.Calendar" factory-method="getInstance" scope="prototype" />
factory-method属性を指定すると、applicationContext.getBean("dateFactory") を呼び出したときの動作が変わります。
factory-methodで指定したCalendar.getInstance() が呼ばれ、そのメソッドが返すオブジェクトをgetBean()が返します。
ここでscope="prototype" と組み合わせると、毎回違うオブジェクトが生成されます。
ですので上記の場合は、getBean()が呼ばれた時刻のCalendarオブジェクトを取得します。
もし、scopeを省略すると、singletonになりますので、Contextファイルを読み込んだときの時刻を毎回取得することになります。
また、lazy-init属性をtrueにすればsingletonに対してもgetBean()を初めて呼び出したときの時刻になるはずです。
<注意点>
この方法は、制限があります。
メソッドに引数がない場合にのみしか利用できません。
自作でFactoryを作成する方法
こちらが通常の方法になるかと思います。
FactoryBeanをimplementsして、Factoryクラスを自作します。
【Factoryクラスを自作するサンプル】
public class DateFactory implements FactoryBean<Calendar>{
@Override
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
@Override
public Class<Calendar> getObjectType() {
return Calendar.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
生成するオブジェクト(getObject())と、クラスの型(getObjectType())と、scopeのタイプ(isSingleton())を実装します。
上記のクラスをContext.xmlに通常のbeanとして記述すれば、Calendarを生成することになります。
SpringはFactoryBeanをimplements したクラスを見つけると、自動的にFactoryとみなします。
その他のタグ
utilのタグのサンプル
beanの他にもJavaの通常よく使うクラスは記述が楽になるように、設定が用意されています。
使用のためには、XMLのnamespaceを記述しておく必要があります。
【util のタグのサンプル】
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
">
<util:list id="nameList" value-type="java.lang.String">
<value>suzuki</value>
<value>sato</value>
</util:list>
<util:map id="nameMap" key-type="java.lang.String" value-type="java.lang.String">
<entry key="taro" value="太郎" />
<entry key="hana" value="華" />
</util:map>
</beans>
ここではutil のうち、list, mapをサンプルとして記述しました。
それぞれ、Listオブジェクトと、Mapオブジェクトが生成されます。
Mapについては、LinkedHashMapが生成されるため、値の順番が記述と同じになります。
List、Map内に設定するオブジェクトの型は、上記のように明示的に指定することも省略することもできます。
省略した場合はStringになります。
Propertiesの特殊記法
setterにPropertiesクラスがある場合、通常では以下のように記述します。
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<entry key="jdbc.driver.className" value="=com.mysql.jdbc.Driver"/>
<entry key="jdbc.url" value="jdbc:mysql://localhost:3306/mydb"/>
</property>
</bean>
Springでは以下のように記述することもできます。
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
ワイルドカードで複数のファイルをDIする方法
Springでは簡単にワイルドカード指定で複数のファイルを取得できます。
クラス側での準備とDIコンテナでの指定方法を簡単に見ています。
【クラスでの準備例】
protected Resource[] files;
public void setFiles(Resource[] files) {
this.files = files;
}
filesプロパティに複数ファイルを設定することを目標にします。
ポイントは、Resourceインタフェースの配列を使用することです。
Resourceインターフェースのパッケージまで含めた名称は、org.springframework.core.io.Resourceです。
【DIコンテナ(XMLファイル)での記述例】
<bean id="sample" class="com.SampleClass">
<property name="files" value="file:c:/*.txt"/>
</bean>
他の記事で書きましたが、SpringのDIコンテナでは値の設定時に自動でいろいろしてくれるものがあります。
例えば文字列の配列などの場合、value="a,b,c"というカンマ区切りの文字列を設定すると、配列[0]=a, 配列[1]=b, 配列[2]=c
といった具合に設定してくれます。
Resourceインターフェースについても同様に特殊なことをしてくれます。
上記のようにワイルドカード指定ができ、マッチするファイルを全て自動でResource配列に設定してくれます。
便利ですよね。
Tipsに他にも便利な機能を紹介していますので、一度目を通していただければと思います。
Created Date: 2011/10/12