Spring Frameworkでの画面開発基礎

1.画面遷移(GET、POST)

Springでの画面遷移は、Controllerのメソッドに@RequestMappingを設定します。フォーム送信時のGET/POSTはアノテーションのパラメータで指定します。

@Controller
public class HogeController {

	@RequestMapping(value = "/hoge/input", method = RequestMethod.GET)
	public String input(Model model) {
		return "hoge/input";
	}

	@RequestMapping(value = "/hoge/confirm", method = RequestMethod.POST)
	public String confirm(Model model) {
		return "hoge/confirm";
	}
}

ThymeleafでのURL指定例は以下の通りです。

<a th:href="@{/hoge/input}">
<form th:action="@{/hoge/confirm}" method="post">

2.パラメータの受け渡し(フォーム)

画面間のパラメータ受け渡しは、アクションフォームを介して行います。

アクションフォームを作成し、フィールドを追加します。Thymeleafの仕様により、getter/setterは必須となります。

public class HogeForm implements Serializable {

	private String hogeName;

	public String getHogeName() {
		return hogeName;
	}

	public void setHogeName(String hogeName) {
		this.hogeName = hogeName;
	}
}

メソッドの引数にアクションフォームを追加します。アクションフォームには@ModelAttributeを設定します。

@Controller
public class HogeController {

	@RequestMapping(value = "/hoge/input", method = RequestMethod.GET)
	public String input(@ModelAttribute HogeForm form, Model model) {
		return "hoge/input";
	}

	@RequestMapping(value = "/hoge/confirm", method = RequestMethod.POST)
	public String confirm(@ModelAttribute HogeForm form, Model model) {
		return "hoge/confirm";
	}
}

3.入力チェック(バリデーション)

SpringでのバリデーションはBean Validationを使用して実装するのが一般的です。

まずはBean ValidationおよびHibernate Validatorを使用するため、pom.xmlに定義を追加します。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.miyadai.app</groupId>
  <artifactId>study-spring</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
            :
    <!-- Bean Validation API version -->
    <validation-api.version>2.0.1.Final</validation-api.version>
    <!-- Hibernate Validator version -->
    <hibernate-validator.version>5.3.4.Final</hibernate-validator.version>
            :
  </properties>
  <dependencies>
            :
    <!-- Bean Validation API -->
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>${validation-api.version}</version>
    </dependency>
    <!-- Hibernate Validator -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>${hibernate-validator.version}</version>
    </dependency>
            :
  </dependencies>
</project>

アクションフォームの各フィールドにアノテーションを設定することでチェックを追加します。

public class HogeForm implements Serializable {

	@NotBlank
	@Size(min = 1, max = 256)
	private String hogeName;

	public String getHogeName() {
		return hogeName;
	}

	public void setHogeName(String hogeName) {
		this.hogeName = hogeName;
	}
}

コントローラのメソッド引数のアクションフォームに@Validatedを設定し、さらにチェック結果を取得するための引数を追加します。また、チェック結果を判定し、表示する画面を切り替えるなどの処理を追加します。

@Controller
public class HogeController {

	@RequestMapping(value = "/hoge/input", method = RequestMethod.GET)
	public String input(@ModelAttribute HogeForm form, Model model) {
		return "hoge/input";
	}

	@RequestMapping(value = "/hoge/confirm", method = RequestMethod.POST)
	public String confirm(
			@ModelAttribute @Validated HogeForm form, BindingResult result, Model model) {

		if (result.hasErrors()) {
			return input(form, model);
		}

		return "hoge/confirm";
	}
}

HTML(Thymeleaf)ではエラーメッセージを表示させる処理を記述します。

<form th:action="@{/hoge/confirm}" th:object="${hogeForm}" method="post">
    <input type="text" th:field="*{hogeName}">
    <p th:if="${#fields.hasErrors('hogeName')}" th:errors="*{hogeName}"></p>
</form>

メッセージを日本語化及びカスタマイズするため、WebMvcで使用するバリデータをオーバーライドします。

@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {

	@Bean
	public MessageSource messageSource() {
		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
		// ValidationMessage.propertiesを使用
		messageSource.setBasename("classpath:ValidationMessages");
		// メッセージプロパティの文字コードを指定
		messageSource.setDefaultEncoding("UTF-8");
		return messageSource;
	}

	@Bean
	public LocalValidatorFactoryBean validator() {
		LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
		localValidatorFactoryBean.setValidationMessageSource(messageSource());
		return localValidatorFactoryBean;
	}

	@Override
	public Validator getValidator() {
		return validator();
	}
}

hibernate-validator-x.x.x.Final.jar内に含まれるValidationMessages.propertiesを取り出し、src/main/resources配下にコピーします。

メッセージを適宜日本語化し、メッセージに埋め込むフィールド名も定義します。メッセージ内の{0}の部分にフィールド名を差し込むことができます。

# Message.

javax.validation.constraints.AssertFalse.message = must be false
javax.validation.constraints.AssertTrue.message  = must be true
javax.validation.constraints.DecimalMax.message  = must be less than ${inclusive == true ? 'or equal to ' : ''}{value}
javax.validation.constraints.DecimalMin.message  = must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}
javax.validation.constraints.Digits.message      = numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Future.message      = must be in the future
javax.validation.constraints.Max.message         = must be less than or equal to {value}
javax.validation.constraints.Min.message         = must be greater than or equal to {value}
javax.validation.constraints.NotNull.message     = may not be null
javax.validation.constraints.Null.message        = must be null
javax.validation.constraints.Past.message        = must be in the past
javax.validation.constraints.Pattern.message     = {0}には不正な値が設定されています。
javax.validation.constraints.Size.message        = {0}は{min}文字以上、{max}文字以内で入力してください。

org.hibernate.validator.constraints.CreditCardNumber.message        = invalid credit card number
org.hibernate.validator.constraints.EAN.message                   = invalid {type} barcode
org.hibernate.validator.constraints.Email.message                   = not a well-formed email address
org.hibernate.validator.constraints.Length.message                  = length must be between {min} and {max}
org.hibernate.validator.constraints.LuhnCheck.message               = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod10Check.message              = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod11Check.message              = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed
org.hibernate.validator.constraints.ModCheck.message                = The check digit for ${validatedValue} is invalid, ${modType} checksum failed
org.hibernate.validator.constraints.NotBlank.message                = {0}を入力してください。
org.hibernate.validator.constraints.NotEmpty.message                = {0}を入力してください。
org.hibernate.validator.constraints.ParametersScriptAssert.message  = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.Range.message                   = must be between {min} and {max}
org.hibernate.validator.constraints.SafeHtml.message                = may have unsafe html content
org.hibernate.validator.constraints.ScriptAssert.message            = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.URL.message                     = must be a valid URL

org.hibernate.validator.constraints.br.CNPJ.message                 = invalid Brazilian corporate taxpayer registry number (CNPJ)
org.hibernate.validator.constraints.br.CPF.message                  = invalid Brazilian individual taxpayer registry number (CPF)
org.hibernate.validator.constraints.br.TituloEleitoral.message      = invalid Brazilian Voter ID card number

# Field Name.
hogeName=ほげ名

4.例外処理(エラー画面制御)

Webシステムで予期せぬエラーが発生した場合、デフォルトのエラー画面ではセキュリティ上脆弱となるため、エラー画面はカスタマイズする必要があります。

デフォルトのエラー画面では、アプリケーションサーバのバージョンが表示されているケースがあり、攻撃者はこのバージョンのアプリケーションサーバの脆弱性をついて攻撃することができてしまいます。

web.xmlのエラーページ設定でエラー時のURLを定義し、URLに応じたページを用意することでエラー画面をカスタマイズすることが可能です。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
        :
  <error-page>
    <location>/errors</location>
  </error-page>
</web-app>

コントローラではエラー時のURLに沿ったRequestMappingを設定します。

@Controller
public class ErrorController {

	@RequestMapping(value = "errors", method = RequestMethod.GET)
	public String renderErrorPage() {
		return "error";
	}
}

エラー画面用のHTML(Thymeleaf)を用意します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="utf-8">
    <title>エラー</title>
  </head>
  <body>
    <h1>エラー</h1>
    <h3>予期せぬエラーが発生しました。</h3>
    <p>しばらく経ってから再度アクセスしてください。<br>
    問題が解消しない場合は、管理者までご連絡ください。</p>
    <a th:href="@{/}">トップへ戻る</a>
  </body>
</html>

Spring Frameworkの環境構築

1.はじめに

以下の通り、開発環境を構築します(まずは★の部分のみ構築します)。

  • OS:Windows10 Home 64bit
  • Pleiades All in One Eclipse 2020-12 (4.18.0) ★
    • OpenJDK
    • Tomcat 9.0.41
    • Maven 3.6
  • Spring ★
    • Spring Framework 5.2.4.RELEASE
    • Spring WebMVC 5.2.4.RELEASE
  • View Engine ★
    • Thymeleaf 3.0.11.RELEASE
  • Database
    • PostgreSQL
    • MyBatis
  • Logging
    • Logback
  • Test
    • JUnit

2.Eclipseのインストール

Pleiades 日本語プラグインのサイト(https://mergedoc.osdn.jp/)にアクセスし、Pleiades All in Oneをダウンロードします。

(1) サイトにアクセスしたら「Eclipse 2020」をクリックします。

(2) Windows 64bitのJava、Full Editionをクリックします。

(3) ダウンロードしたzipファイルを任意の場所に解凍します。

3.mavenプロジェクトの作成

(1) Eclipseを起動し、メニューから「ファイル」→「新規」→「Maven プロジェクト」を選択します。

(2) 新規 Maven プロジェクトダイアログで「シンプルなプロジェクトの作成」をチェックし、次へボタンをクリックします。

(3) グループId(Javaパッケージ)、アーティファクトId(プロジェクト名)を入力し、完了ボタンをクリックします。

4.pom.xmlの編集

(1) pom.xmlを開き、ソースのエンコーディング、コンパイルJavaバージョンを指定します。各ライブラリのバージョンも定義しておきます。

<properties>
  <!-- Generic properties -->
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>11</maven.compiler.source>
  <maven.compiler.target>11</maven.compiler.target>
  <!-- Servlet version -->
  <servlet.version>4.0.1</servlet.version>
  <!-- Spring framework version -->
  <spring-framework.version>5.2.4.RELEASE</spring-framework.version>
  <!-- Thymeleaf -->
  <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
</properties>

(2) 続いて、Spring MVCに必要なJarファイルの依存関係をdependenciesディレクティブの中に定義します。

<dependencies>
  <!-- Servlet -->
  <dependency>
    <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet.version}</version>
      <!-- コンパイル時のみ依存 -->
      <!-- 実行時はTomcatライブラリを使用するため -->
      <scope>provided</scope>
  </dependency>
  <!-- Spring MVC -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring-framework.version}</version>
  </dependency>
</dependencies>

(3) 次に、Thymeleafに必要なJarファイルの依存関係をdependenciesディレクティブの中に定義します。

<!-- Thymeleaf -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>${thymeleaf.version}</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>${thymeleaf.version}</version>
</dependency>

(4) pom.xmlの編集か完了したら、変更を保存し、プロジェクトの上で右クリック、「Maven」→「プロジェクトの更新」をクリックします。

(5) Maven プロジェクトの更新ダイアログが表示されたらOKボタンをクリックします。

(6) Maven 依存関係に定義したSpring MVC関連のライブラリが追加されます。

5.HelloWorldプログラムの作成

5-1.Bean定義ファイルの作成

(1) src/main/resourcesフォルダ配下にconfigフォルダを作成し、「spring.xml」を作成します。

(2) spring.xmlにビジネスロジックのBean定義を記述します。
※HelloWorldプログラムではビジネスロジックは不要なので、空の定義にします。

<?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">
</beans>

(3) configフォルダ配下に「spring-mvc.xml」を作成します。

(4) spring-mvc.xmlにプレゼンテーション層のBean定義を記述します。

<?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"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
  <!-- Spring MVCの使用を宣言 -->
  <mvc:annotation-driven />
  <!-- Beanクラスが格納されるパッケージのルートを宣言 -->
  <context:component-scan base-package="org.miyadai.app.common" />
  <context:component-scan base-package="org.miyadai.app.study" />
</beans>

5-2.コントローラの作成

(1) src/main/javaフォルダ配下にパッケージ「org.miyadai.app.study.controller」を作成します。

(2) 作成したパッケージ配下に「TopController.java」を作成します。

package org.miyadai.app.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TopController {
  @RequestMapping(value = "/", method = RequestMethod.GET)
  public ModelAndView top(ModelAndView model) {
    model.setViewName("top");
    model.addObject("message", "Hello World!!");
    return model;
  }
}

5-3.ビューの作成

(1) パッケージ「org.miyadai.app.common.config」を作成し、作成したパッケージ配下に「ThymeleafConfig.java」を作成します。

package org.miyadai.app.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
@Configuration
public class ThymeleafConfig {
  @Bean
  public ITemplateResolver templateResolver() {
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setPrefix("/WEB-INF/view/");
    resolver.setSuffix(".html");
    resolver.setTemplateMode("HTML");
    resolver.setCharacterEncoding("UTF-8");
    return resolver;
  }
  @Bean
  public SpringTemplateEngine templateEngine() {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver());
    return templateEngine;
  }
  @Bean
  public ViewResolver viewResolver() {
    ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
    thymeleafViewResolver.setTemplateEngine(templateEngine());
    thymeleafViewResolver.setCharacterEncoding("UTF-8");
    return thymeleafViewResolver;
  }
}

(2) プロジェクトフォルダ直下に「WebContent/WEB-INF/view」というフォルダを作成します。

(3) 作成したフォルダ配下に「top.html」を作成します。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="utf-8">
    <title>Spring サンプル</title>
  </head>
  <body>
    <span th:text="${message}"></span>
  </body>
</html>

5-4.web.xmlの作成

(1) WebContent/WEB-INFフォルダ配下に「web.xml」を作成します。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
</web-app>

(2) web.xmlにビジネスロジックのBean定義ファイルの定義を追加します。

<!-- ビジネスロジックのBean定義ファイル -->
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:/config/spring.xml</param-value>
</context-param>

(3) web.xmlにリスナーの定義を追加します。

<!-- リスナーを登録 -->
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

(4) 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>
</filter>
<filter-mapping>
  <filter-name>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

(5) web.xmlにSpring MVCで処理をするためのディスパッチャーサーブレットの定義を追加します。

<!-- ディスパッチャーサーブレット -->
<servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- Spring MVCのBean定義ファイル -->
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:/config/spring-mvc.xml</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

5-5.Webサービスの起動

(1) Eclipseにサーバービューを表示させます。「ウィンドウ」→「ビューの表示」→「その他」をクリックしてください。

(2) ビューの表示ダイアログで「サーバー」→「サーバー」を選択し、開くボタンをクリックします。

(3) サーバーウィンドウの上で右クリックし、「新規」→「サーバー」を選択してください。

(4) 新規サーバーの定義ダイアログが表示されたら、「Tomcat v9.0 サーバー」を選択し、完了ボタンをクリックします。

(5) プロジェクトの上で右クリックし、プロパティを選択します。

(6) プロジェクト・ファセットを選択し、「ファセット・フォームへ変換…」をクリックします。

(7) 「動的 Web モジュール」をチェックし、適用して閉じるボタンをクリックします。

(8) サーバーウィンドウの追加したサーバーの上で右クリックし、「追加および除去」をクリックします。

(9) 追加および除去ダイアログが表示されたら、アプリケーションを構成済みに追加し、完了ボタンをクリックします。

(10) プロジェクトの上で右クリックし、プロパティを選択します。

(11) デプロイメント・アセンブリーを選択し、追加ボタンをクリックします。

(12) ディレクティブ・タイプの選択ダイアログが表示されたら、「Java ビルド・パス・エントリー」を選択し、次へボタンをクリックします。

(13) Java ビルド・パス・エントリーダイアログが表示されたら、「Maven 依存関係」を選択し、完了ボタンをクリックします。

(14) サーバーウィンドウの追加したサーバーの上で右クリックし、「開始」をクリックします。

(15) サーバー起動途中でファイアウォールの警告が出たらアクセスを許可するボタンをクリックします。

(16) サーバーが起動したら、ブラウザから「http://localhost:8080/study/」にアクセスします。Hello World画面が出たら成功です。