Edited at

StrutsからSpringMVCへの移行まとめ

More than 3 years have passed since last update.


はじめに

社内のWebアプリケーションのフレームワークをStrutsからSpringMVCに移行しました。

移行でやったことをさらっとまとめます。


経緯

フレームワークにStrutsを採用していましたが、

StrutsはEOLを迎え、バグや脆弱性に対しての対応がされなくなりました。

社内とはいえ、そろそろ移行時期かと思い、フレームワークの検討をしました。結果、

 5年後もメンテナンスが継続されてそう!

 既存のコードの流用性が高そう!

ということでSpringMVCに移行することになりました。


環境


  • Struts 1.2

  • Spring 4.2.3.RELEASE

  • JDK 7.0

  • JSP 2.2

  • Apache Tomcat 7.0.55


準備

急いては事を仕損じます。

Spring初心者の場合は、世界に向けてあいさつをするところから始めましょう。

私はEclipseにSpring Tool Suiteのプラグインを追加して色々触ってみました。

以下、SpringMVCを使ってHello Worldはできた前提で進めます。


本番作業

大きく分けてやることは3つです。

1.JSPのタグを変更

2.画面遷移を変更

3.Validationを変更

Springは設定より規約なフレームワークです。

主にStrutsのstruts-config.xmlとvalidation.xmlに設定していた画面遷移まわりの内容を移行していきます。


JSPのタグを変更

タグライブラリの指定

Struts

<%@ taglib prefix="bean" uri="/WEB-INF/struts-bean.tld" %>

<%@ taglib prefix="html" uri="/WEB-INF/struts-html.tld" %>

<%@ taglib prefix="logic" uri="/WEB-INF/struts-logic.tld" %>

Spring

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

タグの変換表

Struts
Spring

bean:define
c:set

bean:message
spring:message

bean:write
c:out

logic:equal
c:if

logic:iterate
c:forEach

html:text
form:input

html:optionsCollection
form:options

html:radio
form:radiobutton

(上記以外の) html:
form:

その他 logic:messagesPresent(エラーメッセージの存在チェック)は

c:if test="${!empty エラー}"のような感じにしました。


画面遷移を変更

struts-config.xmlに設定していた内容をコントローラーに書き換えます。

Struts


struts-config.xml

 <action path="/hello" type="jp.sample.action.HelloAction"

name="HelloForm" scope="request" parameter="start">
<forward name="success" path="/hello.jsp" />
</action>


HelloAction.java

 package jp.sample.action;

public class HelloAction extends Action {
public ActionForward execute(ActionMapping mapping, HelloForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception{
・・・
return(mapping.findForward("success"));
}
}


Spring


HelloAction.java

 package jp.sample.action;

@Controller
public class HelloAction {
@RequestMapping("/hello") // action pathの値
public String hello(Model model) {
  ・・・
return "hello"; // forward pathの値
}
}



Validationを変更

validation.xmlに設定していた内容をBeanValidationに書き換えます。

javax.validationとHibernate Validatorのアノテーションを使用します。

別途ライブラリの追加が必要です。

Struts


validation.xml

 <field property="name" depends="required">

<arg0 key="名前" resource="false">
</field>
<field property="mail" depends="required,minlength">
<arg0 key="メールアドレス" resource="false">
<arg1 key="${var:minlength}" resource="false" />
<var>
<var-name>minlength</var-name>
<var-value>5</var-value>
</var>
</field>


HelloForm.java

 package jp.sample.form;

public class HelloForm {
private String name;
private String mail;

// Getter/Setter
・・・
 }


Spring


HelloForm.java

 package jp.sample.form;

public class HelloForm {
@NotEmpty   // depends="required"のとき
private String name;

@NotEmpty   // depends="required"のとき
@Length(min=5) // depends="minlength"のとき minはvar-valueの値
private String mail;

// Getter/Setter
・・・
 }


主なValidationの変換表

Struts
Spring

depends="required"

@NotNullまたは@NotEmpty

depends="minlength"またはdepends="maxlength"

@Length

depends="invalid"
@Pattern

depends="intRange"

@Maxまたは@Minまたは@Range

depends="creditcard"
@CreditCardNumber

depends="email"
@Email

メッセージ・リソースファイルもSpringに合わせて少し変更。


MessageResources.properties

 # エラーメッセージ

NotEmpty={0}を入力してください。 # {0}はフィールド名
Length={0}は{2}文字以上で入力してください。# {2}はminの値(ここではmaxは省略)

# フィールドの日本語名
name=名前
mail=メールアドレス


コントローラーでValidationするように修正します。


HelloAction.java

 @Controller

public class HelloAction {
@RequestMapping("/hello")
public String hello(@Valid @ModelAttribute HelloForm form,
            BindingResult result, Model model) {
if (result.hasErrors()) {
// エラー時の処理
}
  ・・・
return "hello";
}

JSPにエラーを表示させる部分を追加します。


hello.jsp

    <form:errors path="*" element="div" />


path="*"はすべてのエラーを表示します。

path="フィールド名"にするとそのフィールドのエラーだけを表示できます。

と、ここまでは簡単だったのですが、ちょっと落とし穴がありました。

Strutsの場合は、validation.xmlに書いてある順にエラーを表示してくれていましたが、

Springはフィールド、エラーの種類の順番がランダムでした。

例えば、

error1.jpg

と表示されるときもあれば、

error2.jpg

と表示されるときもあるのです。

これはイマイチだったのでエラーをソートすることにしました。

ソートをいれるとコントローラーはこのような感じです。


HelloAction.java

 @Controller

public class HelloAction {
@RequestMapping("/hello")
public String hello(@Valid @ModelAttribute HelloForm form,
            BindingResult result, Model model) {
if (result.hasErrors()) {
// ソート処理
List<FieldError> errors = sortErrors(result);
// エラーメッセージを格納
model.addAttribute("errors", errors);
}
  ・・・
return "hello";
}

private List<FieldError> sortErrors(BindingResult result) {
// エラー表示するフィールドの順番
final String FIELD_ORDER[] = {"name", "mail"};
// エラー表示するエラー種類の順番
final String ERROR_TYPE_ORDER[] = {"NotEmpty", "Length"};

List<FieldError> sortedErrors = new LinkedList<FieldError>();

for (String field : FIELD_ORDER) {
// フィールド別にエラーを取得
List<FieldError> fieldErrors = result.getFieldErrors(field); // ①
int errorSize = fieldErrors.size();
if (errorSize == 0) {
continue;
}
// エラーが1つの場合
if (errorSize == 1) {
sortedErrors.addAll(fieldErrors);
continue;
}
// エラーが複数の場合、エラー種類別にソート
for (String errorType : ERROR_TYPE_ORDER) {
for (FieldError fieldError: fieldErrors) {
// エラー種類を比較
if (errorType != null &&
errorType.equals(fieldError.getCode())) { // ②
sortedErrors.add(fieldError);
break;
}
}
}
}
return sortedErrors;
}
}


result.getFieldError(フィールド名);で対象のフィールドのエラーが取得できること

fieldError.getCode();でエラーの種類が取得できることがポイントです。

JSPのエラー表示部分も変更します。


hello.jsp

    <c:forEach items="${errors}" var="error">

<spring:message message="${error}" /><br>
</c:forEach>

これでエラーの順番が固定されて表示できるようになりました。

error3.jpg


おまけ

個人的にハマったところのメモです。


最後に

Springは巨大なフレームワークなので、どこから取りかかれば良いか、最初は悩むと思います。

知識ゼロからここまでくるのに結構苦労しましたが、新しいことを勉強するのは楽しいです。

この記事が少しでも何かのお役に立てれば幸いです♥