04.Spring-Struts2の実際の利用サンプル
(設定ファイルによる構築)

概要

さて、やっと実際のサンプルに入っていけます。

このサンプルを見る前に、必ず、前の章の記事をお読みください。

少なくともこちらはお読みください。

まずは、設定ファイルを使用するサンプルを見ます。

次の記事で設定ファイルを記述しないゼロコンフィギュレーションのサンプルを見てみたいと思います。

目標

まず、以下のサンプルの目標(ゴール)を示します。

目標は、ユーザ情報をDBから取得して画面に表示し、編集する機能を作成することです。

【動作について】

入力画面 ⇒ 確認画面 ⇒ 完了画面、という画面を作成していきます。

画面とActionの連携のパターンとしては、「複数の画面に1つのActionを結びつけるパターン」を使用します。

(パターンについてはこちらの記事を参照)

【実装方法について】

上記の動作をStruts2と、Springを連携させて作成していきます。

必要なライブラリや、web.xmlの書き方が分からない方はこちらをご覧ください。

ちなみに、Springとの連携は、struts2-spring-plugin-x.x.x.jarをクラスパスに入れるだけです。

その他の設定は特に必要ありません。

【メッセージの設計】

記述を簡易にするため、メッセージリソースはグローバルのファイルを1つだけにしました。(実戦での設計方法などは他の記事に書きます。)

また、メッセージリソースは、画面表示の項目名、妥当性チェックのエラーメッセージ、などに利用しましたが、

メッセージのキー名を同じにすることで設定は1つで使いまわすようにしました。

キー名のルールは、今回は”モデル名.プロパティ名”にしました。(例:"user.name")

【例外処理について】

今回は例外処理の設定はしません。

また別の記事で扱おうかと思っています。

web.xmlの設定は、こちらをご覧ください。

※一番最後の「動作」の章に画面を載せましたので、先にそちらを見てもらうと動作がイメージしやすいかも知れません。

※実行するときは一部だけコピーして使用するとうまく動作しません。いくつかの設定はお互いに関連しています。

使用サンプル

<画面処理(Action)サンプル: test.actions.UserUditAction>

package test.actions;

import javax.annotation.Resource;

import org.apache.struts2.interceptor.validation.SkipValidation;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.context.annotation.Scope;

import test.business.UserService;

import test.models.User;

import com.opensymphony.xwork2.Action;

import com.opensymphony.xwork2.ActionSupport;

/**

* ユーザ編集画面処理。

* Springのスコープはプロトタイプにすること。

*/

@Scope(BeanDefinition.SCOPE_PROTOTYPE)

public class UserEditAction extends ActionSupport {

//SpringによるDIで設定するプロパティ

@Resource

private UserService userService;

//画面情報の保持---------------------------------------------

private User user;

private String remarks;

public User getUser() {

return user;

}

public void setUser(User user) {

this.user = user;

}

public String getRemarks() {

return remarks;

}

public void setRemarks(String remarks) {

this.remarks = remarks;

}

//Actionの処理------------------------------------------------

//入力画面(すぐ下のアノテーションは、妥当性チェックをさせないためのものです)

@SkipValidation

public String doInput() throws Exception {

//値を取得する。

User user = this.userService.getUser();

this.user = user;

//結果コードを返す

return Action.INPUT;

}

//確認画面

public String doCert() throws Exception {

//結果コードを返す

return "cert";

}

//完了画面

public String doFinish() throws Exception {

userService.updateUser(this.user);

//結果コードを返す

return "finish";

}

}

Actionクラスには、画面の情報と処理の2つを記述します。

見ていただくと分かりますが、Userクラスをそのままプロパティにできています。

doFinish()を見ていただければ分かりますが、リクエストパラメタからUserオブジェクトへ値の設定を行っていません。

それでも、この記述で正しく動作します。

<Actionの妥当性チェックサンプル: test/actions/UserEditAction-validation.xml>

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC

"-//Apache Struts//XWork Validator 1.0.3//EN"

"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

<!-- 【DTDの注意点】

DOCTYPEは、strutsのDOCTYPEで宣言してください。

www.opensymphony.comで指定すると、UnknownHostExceptionが出ることがあります。

-->

<validators >

<field name="remarks" >

<field-validator type="stringlength">

<param name="minLength">0</param>

<param name="maxLength">10</param>

<message key="errors.stringlength" />

</field-validator>

</field>

<!--

viditor・・・model用の妥当性チェックファイルを利用するタイプ。

appendPrefix・・・モデルのvalidationファイルでfieldの記述に自動的にnameをつける。

下記設定では、trueのとき"id"、falseのとき"user.id"となる。

-->

<field name="user">

<field-validator type="visitor">

<param name="appendPrefix">false</param>

<message/>

</field-validator>

</field>

</validators>

妥当性チェックは、Actionクラスにアノテーションで直接記述する方法も用意されています。ここではファイルの方法を使いました。

ファイルの方法では、Struts1の妥当性チェックと似た形式で記述します。

field-validatorのtype属性には、チェックの型を指定します。

stringlengthは文字列の長さを扱います。 visitor は、対象クラス(ここではUser)の妥当性チェックを参照しに行きます。

詳しくは妥当性チェックの記事で扱おうと思います。

<モデルとその妥当性チェックサンプル: test.models.User , test/models/User-validation.xml>

public class User {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

}

【Userクラスの妥当性チェック: User-validation.xml】

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC

"-//Apache Struts//XWork Validator 1.0.3//EN"

"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

<validators >

<!-- String ⇒ int 型変換エラー時のメッセージ -->

<validator type="conversion" short-circuit="true">

<param name="fieldName">user.age</param>

<param name="repopulateField">true</param>

<message key="errors.int" />

</validator>

<!-- プロパティの妥当性チェック -->

<field name="user.name" >

<field-validator type="stringlength">

<param name="minLength">3</param>

<param name="maxLength">10</param>

<param name="trim">true</param>

<message key="errors.stringlength" />

</field-validator>

</field>

<field name="user.age" >

<field-validator type="int">

<param name="min">20</param>

<param name="max">30</param>

<!-- messageタグの間にキーが見つからないときのデフォルト値を設定することもできます。 -->

<message key="errors.IntRange">${min}~${max}の数値を入力せよ</message>

</field-validator>

</field>

</validators>

モデルとその妥当性チェックの設定ファイルは、パッケージの同じ階層に置きます。

また、visitorにより、他の妥当性チェックの設定から参照して使用できます。

今回の場合、appendPrefix=falseにしているので、フィールド名に"user.name"のように"user."というプレフィックスも指定します。

<ビジネスロジックのサンプル: test.business.UserService>

public class UserService {

private UserDao userDao;

public UserDao getUserDao() {

return userDao;

}

public void setUserDao(UserDao userDao) {

this.userDao = userDao;

}

public User getUser(){

return this.userDao.getUser();

}

public void updateUser(User user){

this.userDao.updateUser(user);

}

}

特に特筆することはありません。通常のビジネスロジックを作ります。

<Daoのサンプル: test.dao.UserDao>

public class UserDao {

public User getUser(){

//ここではハードコードしていますが、本当はDBから値を取得します。

User user = new User();

user.setName("未華子");

user.setAge(21);

return user;

}

public void updateUser(User user){

//DBに値を更新する処理を書きます。ここでは省略。

}

}

特筆することはありません。通常のDaoを作ります。

<Spring設定ファイルのサンプル: WEB-INF/spring/applicationContext.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"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- struts2用

prototypeにすること。prototypeにする主な方法は以下の2つ。

・@Scope(BeanDefinition.SCOPE_PROTOTYPE)をクラスに指定する

・scope-resolverにプロトタイプにする自作クラスを作成&設定する。

-->

<context:component-scan base-package="test.actions" annotation-config="true" >

<context:include-filter type="regex" expression=".*Action" />

</context:component-scan>

<!-- サービス・DAOの登録 -->

<bean id="userService" class="test.business.UserService">

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

</bean>

<bean id="userDao" class="test.dao.UserDao"></bean>

</beans>

Springの設定は重要な事項です。

ActionクラスのSpringへの読み込みは、通常は自動(component-scan)で行います。

もちろん手で1つずつ設定しても良いですが、数が多いので大変です。

【注意点】

特に注意するのは、Struts2ではActionをリクエストの度に生成することが前提になっていることです

Springを使用しない場合は、ActionもStruts2が生成しますが、そのときもリクエスト毎にnewします。

Springを使用する場合は、それを当然、Springが行わなければなりません。

方法は上記コード中のコメント内に書きましたとおりです。

ここでは、@Scopeを使用する方法を取りました。Actionクラスに記述していますので見てみてください。

scope-resolverを使用する方法も記述しようかと思いましたが、他の方が書かれてましたのでリンクを記載します。

http://d.hatena.ne.jp/takahashikzn/20090713/1247486970

<Struts2の設定ファイルのサンプル: src/struts.xml>

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<!-- これをtrueにするとJSP内のs:submit で、method="hello"のようなメソッド指定ができる -->

<constant name="struts.enable.DynamicMethodInvocation" value="true" />

<!-- WEBの言語設定 -->

<constant name="struts.locale" value="ja"/>

<!-- グローバルなプロパティファイルの指定 -->

<constant name="struts.custom.i18n.resources" value="messages" />

<!-- 一応、無設定を無効にする。これでActionクラスを自動的に検索しなくなる。 -->

<constant name="struts.convention.action.disableScanning" value="true"/>

<!-- ognl式でstaticメソッドを使用できるようにするかどうか(使用しない場合はfalse) -->

<constant name="struts.ognl.allowStaticMethodAccess" value="true" />

<!-- Action名に/を使用して階層を実現できるようにするかどうか。-->

<constant name="struts.enable.SlashesInActionNames" value="true" />

<constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/>

<!--

URLパスとActionのマッピング。

-->

<package name="struts2test" extends="struts-default">

<!-- インターセプターの変更。デフォルトのコピーからconversionErrorを除外 -->

<interceptors>

<interceptor-stack name="transact" >

<interceptor-ref name="exception"/>

<interceptor-ref name="alias"/>

<interceptor-ref name="servletConfig"/>

<interceptor-ref name="i18n"/>

<interceptor-ref name="prepare"/>

<interceptor-ref name="chain"/>

<interceptor-ref name="scopedModelDriven"/>

<interceptor-ref name="modelDriven"/>

<interceptor-ref name="fileUpload"/>

<interceptor-ref name="checkbox"/>

<interceptor-ref name="multiselect"/>

<interceptor-ref name="staticParams"/>

<interceptor-ref name="actionMappingParams"/>

<interceptor-ref name="params">

<param name="excludeParams">dojo\..*,^struts\..*</param>

</interceptor-ref>

<!--interceptor-ref name="conversionError"/-->

<interceptor-ref name="validation">

<param name="excludeMethods">input,back,cancel,browse</param>

</interceptor-ref>

<interceptor-ref name="workflow">

<param name="excludeMethods">input,back,cancel,browse</param>

</interceptor-ref>

</interceptor-stack>

</interceptors>

<default-interceptor-ref name="transact" />

<!--

resultは、nameなしの設定がデフォルトでinputを含むはずであるが、

なぜかSpringと連携するとname="input"を記述しないとうまく動作しないようなので注意。

-->

<action name="userEdit" class="userEditAction" method="doInput">

<result>/WEB-INF/content/userInput.jsp</result>

<result name="input">/WEB-INF/content/userInput.jsp</result>

<result name="cert">/WEB-INF/content/userCert.jsp</result>

<result name="finish">/WEB-INF/content/userFinish.jsp</result>

</action>

</package>

</struts>

struts.xmlには、主に、constant(定数)、package(URLのパスとActionクラスのマッピング)を設定します。

【Springとの連携】

Springとの連携自体はspringとの連携プラグインのjarファイルをインストールするだけです。

actionタグのclass属性を見てみてください。

userEditActionと設定されています。本来、class属性はパッケージ名まで含めたクラス名で設定します。

ですので、本当は test.actions.UserEditAction と書かなければ正しく動作しません。

しかし、Springと連携すると、SpringのDIコンテナ上のbean名を指定することができます。

【constantタグ】

記述した内容については、上記のコメントに記述していますので参考にしていただければと思います。

disableScanningだけ触れておきます。

これは、設定なしの、Actionクラスを自動検索するかどうかを設定します。

デフォルトは「自動検索する」になっています。

もし、設定なし(ゼロコンフィギュレーション)を使用する気がない場合は、trueにしておいた方が良いと思います。

【インターセプター(interceptor)】

上記の設定では、conversionErrorインターセプターを除外しています。

このインターセプターはStringからintなどの型変換エラー時にエラーを溜め込みます。

妥当性チェックのconversionでも型変換エラーを溜めるため、2回同じエラーメッセージが出力されてしまいます。

そこで今回は、conversionErrorインターセプターを除外する方法でこの問題の回避しました。

【packageタグ】

packageタグは、内部のactionタグにより、URLのパスとActionを結び付けます。

actionタグのname属性が、URLになります。

上記の場合、/ルート/userEdit.action がURLのパスになります。

<resultタグについて>

resultは、処理結果コードとViewのマッピングを行います。

例えば、ActionのメソッドdoCert()メソッドを見ていただくと、"cert" が返却されるのが分かると思います。

これが処理結果コードです。

doCert()の場合は、/WEB-INF/content/userCert.jspをViewとして使用することになります。

詳しくは他の記事で触れようと思っています。

<method属性>

method属性は、Actionクラスが呼ばれたときに、通常はメソッド名を指定するのですが、

その指定がなかったときに実行するメソッドを指定します。

つまり、デフォルトの実行メソッドです。

<メッセージリソースのサンプル: src/messages_ja.properties>

#エラーメッセージ(OGNLも使用できます

errors.IntRange=${min}以上、${max}以下の数値を入力してください。

errors.stringlength=${minLength}以上、${maxLength}文字以下で入力してください。

#conversionエラー用のメッセージ(OGNLも使用できます)

errors.int=${getText(fieldName)}は、数字で入力してください。

#モデルのプロパティ名

user.name=ユーザ名

user.age=年齢

必要なメッセージを設定しておきます。

ここでは、妥当性チェックと、画面の項目名表示用に値を設定しています。

ファイル名は、struts.xmlのconstantタグで設定しています。

Struts2の妥当性チェックでメッセージを利用する場合、上記のようにOGNL式も使用できます。

OGNL式を使用するときは、${ }で記述します。JSPでOGNL式を使用するときは%{ }のため、記述に注意です。

<各種画面(JSP)のサンプル: WEB-INF/content/userInput.jspなど>

【入力画面: userInput.jsp】

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>

<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<link rel="stylesheet" type="text/css" href="sample.css" />

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>ユーザー情報入力</title>

</head>

<body>

<div style="text-align:center;">ユーザー情報入力</div><br>

<s:form action="userEdit" method="POST" theme="xhtml">

<!-- key属性でメッセージリソースから項目名を設定する方法(値も設定されます) -->

<s:textfield key="user.name" />

<!-- OGNLでメッセージリソースを利用する方法もあります -->

<s:textfield label="%{getText('user.age')}" name="user.age"/>

<!-- もちろん、直接項目名を指定することもできます。 -->

<s:textfield label="備考" name="remarks" />

<!-- ActionクラスのdoCert()メソッドを呼び出します。 -->

<s:submit value="確認" method="doCert"/>

</s:form>

</body>

</html>

Struts2では、s:で始まるタグライブラリが用意されています。

特にStruts1を使用していた方は、あまりにStruts1と違いすぎで戸惑うかもしれません。全く別の使用の仕方になります。

Struts2のタグライブラリの記事で詳しくは触れようと思います。注意点もありますので必ずお読みください。

ここでは、s:textfield が、項目名と、inputタグを作成することだけ把握していれば十分です。

<OGNL式について>

s:textfieldタグを見ると、label="%{getText('user.name')}" という記述があります。

%{ }は、OGNL式です。getText()は、Actionクラスに持っているメソッドで、メッセージリソースを取得できます。

OGNL式はJSPのEL式のような式言語で、POJOのプロパティを取得するだけでなく、メソッドの呼び出しなどもできます。

【確認画面: userCert.jsp】

<%@ page language="java" pageEncoding="utf-8"%>

<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<link rel="stylesheet" type="text/css" href="sample.css" />

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>ユーザー情報確認</title>

</head>

<body>

<div style="text-align:center;">ユーザー情報確認</div><br>

<s:form action="userEdit" method="POST" theme="simple">

<table class="wwFormTable">

<tr>

<td><s:text name="user.name"/><br></th>

<td><s:property value="user.name"/> <s:hidden name="user.name"/><br></td>

</tr>

<tr>

<td><s:text name="user.age"/><br></th>

<td><s:property value="user.age"/> <s:hidden name="user.age"/><br></td>

</tr>

<tr>

<td>備考<br></th>

<td><s:property value="remarks"/> <s:hidden name="remarks"/><br></td>

</tr>

</table>

<div align="left">

<s:submit value="完了" method="doFinish"/>

</div>

</s:form>

</body>

</html>

【完了画面: userFinish.jsp】

<%@ page language="java" pageEncoding="utf-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>ユーザ情報完了</title>

</head>

<body>

<div style="text-align:center;">ユーザ情報完了</div><br>

完了しました。

</body>

</html>

<スタイルシートのサンプル: WebContent/sample.css>

table.wwFormTable{

empty-cells: show;

border:2px solid #999999;

border-collapse: collapse;

padding:2px;

}

table.wwFormTable td{

empty-cells: show;

border:2px solid #999999;

border-collapse: collapse;

padding:2px;

}

.errorMessage{

color: red;

font: bold;

}

詳しくはタグライブラリの記事で触れますが、Struts2ではテーマ(theme)という概念があります。

テーマは、あるタグ(例: s:textfield ) を指定したときにどのようなデザインを出力するかを指定するものです。

カスタマイズもできますが、Struts2がデフォルトでいくつかテーマを持っています。その中で決められたCSSでHTMLを出力します。

そのCSSを上記のように定義することで好きなデザインに変更します。

以上で、動作するはずです。

動作

では、サンプルがどのような動作をするかを見ておきましょう。

【URL】

以下のURLにアクセスしてください。

http://localhost:8080/struts2/userEdit.action

【表示される画面】

【画面デザイン】

入力画面のJSPファイルを確認した人は、「えっ!?」って思いませんでしたか?

JSPファイルには、tableタグを記述していません。

しかし、テーブルが表示されています。

これが、先ほど少し触れたテーマ(theme)の効力です。

themeに、xhtmlを指定すると上記のようになります。つまり、inputタグと、さらに tr , td などのタグが出力されます。

もし、simpleを指定すると、テーブルタグは出力せず、単なるinputタグだけの出力なります。(確認画面参照)

Struts1と同様にtilesも使用できるようですので、かなりViewでできることが増えました。

Created Date: 2012/01/21