Spring Tips

概要

ここではSpringでよくやることをTipsとして簡単に見ていこうと思います。

結構機能が用意されていて、自作する手間がかなり省けることに気づきます。

ここでは詳しい記述は避け、なるべくたくさんのTipsを記述することを目標にします。詳しくはWebで調べていただければと思います。

何かヒントになれば幸いです。

Spring DI編

外部定義propertiesファイルを利用する方法

Springの設定ファイルはjarやwarファイルの中に入れてしまうため、後で定数を変更したい場合にjarファイルなどを展開しないといけなくなってしまいます。

しかし、そもそもSpringは外部定義の実装を楽にすることも目的のひとつですから、機能が用意されています。

【Spring設定ファイルのサンプル】

<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

<property name="location" value="file:c:/test/batch.properties" />

<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />

<property name="ignoreUnresolvablePlaceholders" value="true" />

<property name="order" value="1" />

</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="${batch.jdbc.driver}" />

<property name="url" value="${batch.jdbc.url}" />

<property name="username" value="${batch.jdbc.user}" />

<property name="password" value="${batch.jdbc.password}" />

</bean>

【外部定義ファイル(c:/test/batch.properties)のサンプル】

batch.jdbc.driver=org.hsqldb.jdbcDriver

batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true

batch.jdbc.user=sa

batch.jdbc.password=pass

Spring設定ファイルのplaceholderPropertiesのlocationで、プロパティファイルのパスを設定しています。

そして、プロパティファイルに設定されたキー名を使用して、dataSourceを設定しています。

${batch.jdbc.driver}は、"org.hsqldb.jdbcDriver"に置き替わります。

複数のプロパティファイルを指定することもできますし、環境変数やシステムプロパティを使用することもできます。

beanタグで使用できるpropertyのnameを見る方法

eclipseを使用している場合、Spring用のプラグインを使用できます。

以下のサイトなどをプラグインとしてインストールしておくと便利かと思います。

http://dist.springframework.org/release/IDE

AOPで織り込みした箇所を確認できたり、リファクタリングしたときに自動でSpring設定ファイルを変更してくれたりします。

また設定ファイル上の

<property name="..." value="" />

タグのnameの""の間にカーソルを合わせ、Ctrl+スペースを押すと、クラスが持っているプロパティ名(setter名)を表示してくれます。

もちろんbeanタグのclass属性にクラスを先に設定しておく必要があります。

Springに設定するbeanクラスの妥当性チェックをする方法

Spring設定ファイルに設定したクラスを自動で妥当性チェックする機構が用意されています。

方法は簡単で、以下のインターフェースをimplementsし、チェックの実装を記述するだけです。

org.springframework.beans.factory.InitializingBean

implementsすると、SpringはafterPropertiesSet()メソッドを自動的に呼び出します。

このメソッドに以下のようなチェックを実装すればOKです。

【妥当性チェックのサンプル】

@Override

public void afterPropertiesSet() throws Exception {

Assert.notNull(this.delegationDecision, "delegationDecision must not be null.");

Assert.notEmpty(this.map, "map must not be empty.");

}

Assertのパッケージは、org.springframework.util.Assertとなります。

Spring設定で他のbeanのpropertyの設定値をコピーする方法

Springの設定ファイルの書き方です。

大きな開発になると同じクラスを一部のproperty値だけ上書きして使用したくなることが多々あります。

こんなときはparent属性を使用します。

【設定ファイルのサンプル】

<bean id="userService2" parent="userService">

<property name="user" value="hanako"/>

</bean>

<bean id="userService" class="com.my.Service" abstract="true">

<property name="dataSource" ref="dataSource" />

<property name="user" value="taro"/>

</bean>

上記userService2では、dataSourceプロパティが省略されています。しかし、実際にはuserServiceと同じdataSourceが設定されています。

他のSpring設定ファイルを取り込んで使用する方法

Springでは、Spring設定ファイルを使いまわす方法が用意されています。

共通的なSpring設定ファイルを用意しておき、自分の使用するSpringファイルにインポートする方法です。

【インポートする設定ファイルのサンプル】

<import resource="classpath:/com/my/common-context.xml"/>

上記では、クラスパス上のファイルを指定してインポートしています。

当然、接頭子として"file:"を指定すればローカルパス上のファイルも指定できます。

カンマ区切りの文字列を配列としてbeanのプロパティに設定する方法

Springでは、プロパティがString[]配列のとき、CSVを自動で配列に展開してくれます。

【設定ファイルのサンプル】

<bean id="userService2" parent="userService">

<property name="stringArray" value="1,2,3"/>

</bean>

上記のようにすると、stringArrayプロパティ(String[])には、String[] = {"1","2","3"} が設定されます。

代入先が配列の場合、Springは自動でカンマ区切り文字列を配列に変換します。

カンマ区切りの文字列をListとして設定する方法

ちょっとパズルのようですが、上記の配列の方法を応用するとCSVをListにすることもできます。

【設定ファイルのサンプル】

<bean id="stringArray" class="java.util.ArrayList">

<constructor-arg index="0" type="java.util.Collection" ><value type="[Ljava.lang.String;">1,2,3</value></constructor-arg>

</bean>

上記のようにすると、stringArrayにはListが設定されます。

stringArray.get(0) = "1" となっています。

まず、valueタグでStirng[]を生成しています。文字列"1,2,3"の代入先が配列(String[])になるので、自動的にカンマ区切りを配列に展開します。

次にSpringの自動変換の機能で、配列からCollectionに変換します。変換後のCollectionをArrayListのコンストラクタの第一引数にすることでListを生成しています。

よくある一般的機能編

ワイルドカードを使った文字列マッチングをする方法

ワイルドカードはよく使いたくなる機能です。

しかし、実装するとなると意外と面倒です。

Springではそんな機能も用意されているんです。

【ワイルドカードによるマッチングをするサンプルコード】

if(PatternMatchUtils.simpleMatch("sys*", "system")){

System.out.println("マッチしました");

}

パッケージまで含めたクラス名は org.springframework.util.PatternMatchUtils です。

詳しくはJavadocを参照ください。

複数のワイルドカードを指定することもでき、いづれかにマッチしていればtrueを返すstaticメソッドも用意されています。

ワイルドカードに使用できるのは、*のみのようです。

また、javadocには記述していませんが、試した感じではエスケープ文字はなさそうです。

それが正しいとすると、*という一文字をマッチングさせることはできなさそうです。

Ant形式のマッチングをする方法

Ant形式とは、以下のようなパスをワイルドカードで指定できる形式です。

/*/sys/abc.txt

/**

普通のワイルドカードとの違いは、*を指定したときに/(スラッシュ)は含まれないということと、**はスラッシュも含めたワイルドカードになっていることです。

このような形式はAnt形式として知られていて、ファイルのパスなどを指定するときに重宝します。

これも使用したくなる機能ですが自作すると結構大変です。Springで用意されているクラスをうまく使いましょう。

【Ant形式のマッチングをするサンプルコード】

AntPathMatcher matcher = new AntPathMatcher();

if(matcher.match("/*/sys*", "/com/sysTem")){

System.out.println("マッチしました");

}

パッケージまで含めたクラス名は、org.springframework.util.AntPathMatcherです。

詳しくはJavadocを参照ください。

自動リロードできるメッセージリソース(propertiesファイル)を使用する方法

メッセージリソースは通常一度ファイルからロードされると、ファイルを変更しても反映されることはありません。

しかし、Springではタイムスタンプを見て変更されていればロードするというクラスを持っています。

【リロードできるメッセージソースのSpring設定サンプル】

<bean id="h" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

<property name="cacheSeconds" value="10" />

<property name="basename" value="file:c:/temp/test" />

</bean>

ファイル名: c:/temp/test_ja.properties

10秒おきにタイムスタンプを見に行くことになります。

詳しくはJavadocを参照ください。

処理時間を測る方法

単体テストなどでは処理時間を測りたい場合があります。

もちろんSpringではそのような機能も用意されています。

【時間を測るサンプルコード】

StopWatch stopWatch = new StopWatch();

stopWatch.start("cane");

//何か処理を記述

stopWatch.stop();

System.out.println(stopWatch.prettyPrint());

パッケージまで含めたクラス名は、org.springframework.util.StopWatchです。

コンソールなどに出力するだけでなく、トータル処理時間も取得できるため、取得後にJUnitのassertで規定時間内に処理が終了しているかどうかもチェックできます。

詳しくはJavadocを参照ください。 stopWatch.getTotalTimeSeconds()。

コマンド起動するアプリのLog4Jのログ出力先をコマンド引数で指定する方法

特にSpringを使用しなくてもできます。でもそれだと面白くないので、両方とも紹介したいと思います。

<Springを使用しない方法のサンプル>

【コマンド起動のサンプル】

java -Dlog.file=c:/temp/test.log ...

【Log4jの設定ファイルのサンプル】

log4j.appender.file.File=${log.file}

実はLog4jの設定ファイルではシステムプロパティを使用できます。ですので、${}で指定してあげるだけです。

引数のシステムプロパティを使用せず、main(String[] args)で受け取ったargs引数をシステムプロパティに設定するという方法でも良いと思います。

例: System.setProperty("log.file", args[0]);

<Springのクラスを使用するサンプル>

【コマンド起動のサンプル】

java -Dlog4j.file=c:/temp/log4j.properties ...

【mainメソッドのコードのサンプル】

Log4jConfigurer.initLogging(System.getProperty("log4j.file"));

システムプロパティでLog4jの設定ファイルのパスを指定して再読み込みします。

Log4jの設定ファイルではログ出力先を指定できるので、少し回りくどいですがコマンド引数からログ出力先を変えられるのと同じような効果があります。

パッケージまで含めたクラス名は、org.springframework.util.Log4jConfigurerです。

パラメタ形式("a=1 b=2")の文字列をPropertiesオブジェクトにする方法

コマンドの引数など、様々な箇所でパラメタ形式が使用されます。

これをPropertiesオブジェクトにするのもSpringならば簡単です。応用すればいろいろできます。

【パラメタ形式文字列をPropertiesオブジェクトにするサンプル】

String target = "a=1 b=2";

String[] ary = StringUtils.delimitedListToStringArray(target, " ");

Properties opts = StringUtils.splitArrayElementsIntoProperties(ary, "=");

ファイルパスからファイル名や拡張子を取得する方法

StringUtilsでは、ファイル名や拡張子を取得することもできます。

まだ他にも色々と機能がありますので、StringUtilsをうまく活用していきましょう。

ただし、使用できるパスのセパレータはスラッシュ("/")ですので、Windowsの"\"で区切られているものはうまくいかないようです。

【ファイル名、拡張子を取得する方法】

System.out.println(StringUtils.getFilename("c:/temp/tes/go.txt"));

System.out.println(StringUtils.getFilenameExtension("c:/temp/tes/go.txt"));

Web編

WebでSpringを使用する方法

WebでSpringを使用するには、リスナーを設定します。

Springは、Tomcatの起動時にApplicationConextを読み込み、ServletContextに設定します。

Springを使用したいクラスはServletContextからApplicationConextを取り出して使用します。

ここでは、リスナーの設定例をみるにとどめます。

【web.xmlの設定サンプル】

<!-- spring用のリスナー -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/applicationContext.xml

/WEB-INF/spring/applicationContext-action.xml

</param-value>

</context-param>

HttpRequestの文字コードをフィルタで設定する方法

Springでは、Webで文字コードを設定する機構も用意されています。

フィルタを使用する方法です。

【web.xmlの設定サンプル】

<!-- エンコード -->

<filter>

<filter-name>CharacterEncodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>utf-8</param-value>

</init-param>

<init-param>

<param-name>forceEncoding</param-name>

<param-value>true</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>CharacterEncodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

詳しくは、CharacterEncodingFilterのjavadocを見てみてください。

リクエスト毎に新しいインスタンスを生成する方法

Webにおいてはリクエスト毎に新しくインスタンスを生成したくなる場合があります。

Springではそれも可能です。

Spring設定ファイル上でscopeを設定し、フィルタを設定すればよいだけです。

【Spring設定ファイルのサンプル】

<bean id="myClass" class="com.my.MyClass" scope="request">

<property name="xxx" value="12"/>

</bean>

【web.xmlの設定サンプル】

<!-- Spring のrequestスコープのためのフィルタ -->

<filter>

<filter-name>requestContextFilter</filter-name>

<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>requestContextFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

Log4Jの設定ファイルをwarの外部におく方法

リスナーを使用することでLog4Jの設定ファイルの読込先を自由に設定することができます。

【web.xmlの設定サンプル】

<context-param>

<param-name>webAppRootKey</param-name>

<param-value>soracane</param-value>

</context-param>

<listener>

<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

</listener>

<context-param>

<param-name>log4jConfigLocation</param-name>

<param-value>file:c:/temp/log4j.properties</param-value>

</context-param>

log4jConfigLocationには環境変数やシステムプロパティも使用できます。

使い方はSpringのDIと同じで、"file:/c:/temp/${USERNAME}/log4j.properties"のように${}で指定します。

また、file:の代わりにclasspath:接頭子も使用できますし、ファイルの拡張子を.xmlにすれば自動でXML読み込みになります。

ApplicationContextを取得する方法

Web上でSpringを使用するとApplicationContextの生成はtomcatのリスナーが行います。

そのため、どこにApplicationContextがあって、どうやって取得すればよいかが分かりにくくなります。

しかし実際には取得は簡単で、以下のようにすればよいです。

【ApplicationContextの取得サンプル】

ServletContext sc = request.getSession().getServletContext();

WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);

※もしアノテーションを使用してもよいのなら、ApplicationContextのプロパティ(setter)を用意して

その前に@Autowired をつければ取得できると思います。

ここでは、アノテーションを使用したくない場合の例を記述しています。

テスト編

Springの設定ファイルを使って効率よくJUnit単体テストをする方法

Springは単体テスト用の部品も持っています。

SpringJUnit4ClassRunnerです。これを使用すると、自分でApplicationContextを作成するコードを記述しなくてよくなります。

また、@Resourceや@Autowiredのアノテーションが使えるので、ApplicationContextから必要なbeanを取得するコードを記述しなくてよくなります。

@Autowired

@Resource

型(クラス)が同じbeanをApplicationContextから探し出して設定します。

もし、ApplicationContext内に同じ型が複数存在する場合、エラーになります。

プロパティ名と同じbean名をApplicationContextから探し出して設定します。

【SpringJUnit4ClassRunnerの使用サンプル】

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = { "test-applicationContext.xml"})

public class SecurityFilterTest{

//test-applicationContext.xmlを読み込んだApplicationContextを自動で設定します。

@Autowired

ApplicationContext applicationContext;

//applicationContext.getBean("memberService")で取得したオブジェクトを設定します。

@Resource

MemberService memberService

@Test

public void test1() throws Exception{

//何かテストを記述

}

}

locationsは、このクラス(SecurityFilterTest)からの相対パスで指定するか、/com/..のように絶対パスを指定することができます。

また、カンマで区切って複数のファイルを設定できます。

【注意点】

Spring2.5.xでは、SpringJUnit4ClassRunnerはJUnit4.4以前のバージョンにしか対応していません。

どうも、JUnit4.5以上ではJUnitのあるクラスが内部クラスになってしまったために、Springではエラーが発生してしまうようです。

Spring3.0以上ではこの不具合は修正されているようです。

JUnit4より前のバージョンではRunWithアノテーションを持っていないため、JUnit4.xのみで使用できます。

結局、Spring2.5.xを使用する場合は、JUnit4.4を使用した方が良さそうです。

Webの単体テストを行う方法

Webのテストのうち、他のフレームワークでは、Filterのテストや、Controllerのテストなどはおざなりなっていました。

それは、テストをするためのツールが無かったことが原因です。

しかし、Springではこのあたりの部品をいくつも用意してくれています。

【部品の例】

・MockHttpServletRequest

・MockHttpServletResponse

・MockFilterConfig

・MockFilterChain

メール送信の単体テストをする方法

Springを使用すると、メール送信を単体テストすることも可能になります。

SpringのMailSender クラスを使用します。

まず、メール送信の実装としてMailSenderを使用します。このクラスはメール送信を手助けしてくれるツールです。interfaceになっているのでテストが用意になるのです。

パッケージ名まで含めた名称は、org.springframework.mail.MailSenderです。

【メール送信機能を持ったクラス実装のサンプル】

class ErrorListener{

private MailSender mailSender;

private SimpleMailMessage simpleMailMessage;

public void setMailSender(MailSender mailSender) { this.mailSender = mailSender ; };

public void setSimpleMailMessage(SimpleMailMessage simpleMailMessage) {

this.simpleMailMessage= simpleMailMessage;

};

public void event() throws Exception {

//何か処理を記述

//メール送信

SimpleMailMessage msg = new SimpleMailMessage();

this.simpleMailMessage.copyTo(msg);

msg.setText("エラーが発生しました。");

this.mailSender.send(msg);

}

}

【テスト用のSpring設定ファイルのサンプル】

<!-- テスト対象クラスの設定 -->

<bean id="errorListener" class="com.my.ErrorListener">

<property name="mailSender" ref="mailSenderMock" />

<property name="simpleMailMessage" ref="simpleMailMessage" />

</bean>

<!-- メールメッセージの設定 -->

<bean id="simpleMailMessage" class="org.springframework.mail.SimpleMailMessage">

<property name="subject" value="テスト" />

<property name="from" value="test@test.co.jp" />

<property name="to" value="test.to@test.co.jp" />

</bean>

<!-- メール送信テスト用のモッククラスの設定 -->

<bean id="mailSenderMock" class="com.my.MailSenderMock" />

【メール送信テスト用のモッククラスのサンプル】

public class MailSenderMock implements MailSender {

@Override

public void send(SimpleMailMessage simpleMessage) throws MailException {

String msg = simpleMessage.getText();

Assert.assertEquals("エラーが発生しました。", msg);

}

@Override

public void send(SimpleMailMessage[] simpleMessages) throws MailException {

}

}

あとはJUnitのテストクラス内で、Spring設定ファイルで記述したerrorListenerを取得してevent()メソッドを呼び出すだけです。

補足ですが、ここではテスト用のサンプルしか載せていません。

ですのでSpringの設定ファイルのmailSenderMockはダミークラスになっていて、メール送信は実際にはできません。

実際にメールを送信するときは、classをorg.springframework.mail.javamail.JavaMailSenderImplに置き換えてください。

また、モッククラスはEasyMockを使用するともっと簡単にテストが可能になります。

Created Date: 2010/11/27