Help us understand the problem. What is going on with this article?

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は巨大なフレームワークなので、どこから取りかかれば良いか、最初は悩むと思います。
知識ゼロからここまでくるのに結構苦労しましたが、新しいことを勉強するのは楽しいです。
この記事が少しでも何かのお役に立てれば幸いです♥

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away