Spring Boot 基本

複数画面をもつSpring BootのWEBアプリケーションを作成してみた

今回は、入力画面・確認画面・完了画面の3画面を含み、HTMLオブジェクトとしてテキストボックス・セレクトボックス・ラジオボタン・チェックボックスを含むWEBアプリケーションを作成してみたので、そのサンプルプログラムを共有する。

様々なサイト上で、テキストとボタンのみを含むSpring BootのWEBアプリケーションのサンプルプログラムは多く見かけたが、開発現場でよく見かける、セレクトボックス・ラジオボタン・チェックボックスも含むようなプログラムはなかなか見つからなかったので、今回、いろいろなフォームオブジェクトを含むサンプルプログラムを作成してみることにした。

前提条件

以下の記事のSpring BootのWEB画面用アプリが作成済であること。

IntelliJ IDEA上でGradleを使ってWeb画面のSpring Bootプロジェクトを作成してみたSpring Bootのプロジェクトを新規作成を「IntelliJ IDEA」のメニューから実施しようとしたところ、無料の「Commun...

また、以下の記事のlombokの設定が完了していること。

IntelliJ IDEAでlombokを使ってみたlombokというライブラリを使うと、JavaBeanクラスにおいて、アノテーション付与するだけで、getterメソッド・setterメ...



やってみたこと

  1. 完成した画面イメージの共有
  2. サンプルプログラムの作成

 

完成した画面イメージの共有

今回は、サンプルプログラムの内容をより理解しやすくするために、先に完成した画面イメージの共有を行う。

まずは、Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスすることで、以下の入力画面を表示する
入力画面1

上記画面のプルダウン(月)の内容は以下の通り
入力画面2

また、上記画面のプルダウン(日)の内容は以下の通り
入力画面3

入力画面の値を、以下のように変更し、「確認」ボタンを押下し、確認画面に遷移
入力画面4

確認画面では、入力画面の値が以下のように表示される。この状態で「送信」ボタンを押下し、完了画面に遷移
確認画面
なお、「戻る」ボタンを押下した場合は、入力画面に戻る。

以下のような完了画面が表示される。「閉じる」ボタンを押下すると、この画面が閉じる
完了画面

「MiniTool Partition Wizard」はパーティション分割・統合・バックアップ・チェックを直感的に行える便利ツールだったハードディスクの記憶領域を論理的に分割し、分割された個々の領域のことを、パーティションといいます。 例えば、以下の図の場合、C/D...

サンプルプログラムの作成

今回作成したサンプルプログラムの構造は以下の通り。
複数画面をもつプログラムの構成

上記のうち、「DemoForm.java」の内容は以下の通り。こちらは画面間で持ち回す項目のformオブジェクトを定義している。

package com.example.demo;

import lombok.Getter;
import lombok.Setter;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Formオブジェクトのクラス
 */
public class DemoForm {

    /** 名前 */
    @Getter
    @Setter
    private String name;

    /** 生年月日_年 */
    @Getter
    @Setter
    private String birthYear;

    /** 生年月日_月 */
    @Getter
    @Setter
    private String birthMonth;

    /** 生年月日_日 */
    @Getter
    @Setter
    private String birthDay;

    /** 性別 */
    @Getter
    @Setter
    private String sex;

    /** 確認チェック */
    @Getter
    @Setter
    private String checked;

    /** 生年月日_月のMapオブジェクト */
    public Map<String,String> getMonthItems(){
        Map<String, String> monthMap = new LinkedHashMap<String, String>();
        for(int i = 1; i <= 12; i++){
            monthMap.put(String.valueOf(i), String.valueOf(i));
        }
        return monthMap;
    }

    /** 生年月日_日のMapオブジェクト */
    public Map<String,String> getDayItems(){
        Map<String, String> dayMap = new LinkedHashMap<String, String>();
        for(int i = 1; i <= 31; i++){
            dayMap.put(String.valueOf(i), String.valueOf(i));
        }
        return dayMap;
    }

    /** 性別のMapオブジェクト */
    public Map<String,String> getSexItems(){
        Map<String, String> sexMap = new LinkedHashMap<String, String>();
        sexMap.put("1", "男");
        sexMap.put("2", "女");
        return sexMap;
    }

}

この中で、各項目のSetter/Getterメソッドは、lombokを利用している。また、セレクトボックス・ラジオボタンで表示するリストについては、getXXXItemsメソッドにより定義していて、それらはMapオブジェクト(キー項目, 値)のリストにより返却される仕組みになっている。



コントローラクラスである「DemoController.java」の内容は以下の通り。

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

/**
 * コントローラクラス
 * 「@SessionAttributes(types = DemoForm.class)」により、
 * 生成したFormオブジェクトをセッションとしてもたせている
 */
@Controller
@SessionAttributes(types = DemoForm.class)
public class DemoController {

    /**
     * Formオブジェクトを初期化して返却する
     * @return Formオブジェクト
     */
    @ModelAttribute("demoForm")
    public DemoForm createDemoForm(){
        DemoForm demoForm = new DemoForm();
        //名前・性別の初期値を設定する
        demoForm.setName("テスト 名前");
        demoForm.setSex("1");
        return demoForm;
    }

    /**
     * 入力画面に遷移する
     * @param demoForm Formオブジェクト
     * @return 入力画面へのパス
     */
    @GetMapping("/")
    public String index(DemoForm demoForm){
        return "input";
    }

    /**
     * 確認画面に遷移する
     * @param demoForm Formオブジェクト
     * @return 確認画面へのパス
     */
    @PostMapping("/confirm")
    public String confirm(DemoForm demoForm){
        return "confirm";
    }

    /**
     * 完了画面へのリダイレクトパスに遷移する
     * @return 完了画面へのリダイレクトパス
     */
    @PostMapping("/send")
    public String send(){
        return "redirect:/complete";
    }

    /**
     * 完了画面に遷移する
     * @param sessionStatus セッションステータス
     * @return 完了画面
     */
    @GetMapping("/complete")
    public String complete(SessionStatus sessionStatus){
        //セッションオブジェクトを破棄
        sessionStatus.setComplete();
        return "complete";
    }
}

ここでは、各画面に遷移する処理を定義しているが、それに加え、画面で持ち回すformオブジェクト(demoForm)を生成し、セッションとしてもたせる機能ももっている。createDemoFormメソッドでformオブジェクトを生成し、ここで初期値の設定も行い、コントローラクラスの先頭のアノテーション「@SessionAttributes(types = DemoForm.class)」により、セッションによる持ち回しができるようにしている。

さらに、createDemoFormメソッドに「@ModelAttribute(“demoForm”)」が指定してあり、これによって、画面を定義するhtmlソースから、demoFormを参照できるようになっている。

また、HTTP通信のGETリクエスト/POSTリクエストの使い分けを、@GetMapping、@PostMappingというアノテーションで実現している。詳細は以下のサイトを参照のこと。
https://dkssksk.com/springbootmapping/

その他、ボタンの二度押し防止のため、sendメソッド内ではPRGパターンを利用している。PRGパターンを利用したサンプルプログラムについては、以下のサイトを参照のこと。

Spring BootでPRGパターンを利用してみたPRGパターンの「PRG」は「Post&Redirect&Get」の略で、Postした後でリダイレクトして画面表示するパターンのことをい...

入力画面である「input.html」の内容は以下の通り。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>入力画面</title>
</head>
<body>
  <p>下記必要事項を記載の上、「確認」ボタンを押下してください。</p><br/>
<form method="post" th:action="@{/confirm}" th:object="${demoForm}">
    名前: <input type="text" th:value="*{name}" th:field="*{name}" />
    <br/>
    生年月日:
    <input type="text" th:value="*{birthYear}" size="4"
           maxlength="4" th:field="*{birthYear}" />年
    <select th:field="*{birthMonth}">
        <option value="">---</option>
        <option th:each="item : *{getMonthItems()}"
                th:value="${item.key}" th:text="${item.value}"/>
    </select>月
    <select th:field="*{birthDay}">
        <option value="">---</option>
        <option th:each="item : *{getDayItems()}"
                th:value="${item.key}" th:text="${item.value}"/>
    </select>日
    <br/>
    性別:
    <for th:each="item : *{getSexItems()}">
        <input type="radio" th:value="${item.key}"
               th:text="${item.value}" th:field="*{sex}" />
    </for>
    <br/>
    入力確認:
    <input type="checkbox" th:value="確認済" th:field="*{checked}" />
    <br/><br/>
    <input type="submit" value="確認" />
</form>
</body>
</html>

ここでは、formタグの「th:object=”${demoForm}”」を指定することで、DemoController.javaで生成したフォームオブジェクト「demoForm」を参照している。また、フォームの各フィールドには「th:field=”*{XXX}”」と指定することで、demoFormオブジェクトのXXXフィールドへのgetterメソッド・setterメソッドや、XXXメソッドにアクセスしている。

プルダウンオブジェクトを「select~optionタグ」で、ラジオボタンを「input」タグで「type=”radio”」を指定することで生成している。さらに、プルダウン・ラジオボタンの各項目は、「th:each」タグ内で、「DemoForm.java」の「getXXXItems」メソッドを呼び出すことで生成している。



確認画面である「confirm.html」の内容は以下の通り。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>確認画面</title>
</head>
<body>
   <p>入力内容を確認し、問題なければ「送信」ボタンを押下してください。</p>
<form method="post" th:action="@{/send}" th:object="${demoForm}">
    <p th:text="'名前: ' + *{name}">ここに名前が表示されます</p>
    <p th:text="'生年月日: ' + *{birthYear} + '年'
                     + *{getMonthItems().get('__*{birthMonth}__')} + '月'
                     + *{getDayItems().get('__*{birthDay}__')} + '日'">
        ここに生年月日が表示されます
    </p>
    <p th:text="'性別: ' + *{getSexItems().get('__*{sex}__')}">
        ここに性別が表示されます
    </p>
    <p th:text="'確認チェック: ' + *{checked}">
        ここに確認チェック内容が表示されます
    </p>
    <input type="submit" value="送信" />
    <input type="button" value="戻る" onclick="history.back();" />
</form>
</body>
</html>

ここでは、例えば「*{getSexItems().get(‘__*{sex}__’)}」という記述をすることで、性別の表示文言「女」を表示している。getメソッド内の引数「__*{sex}__」を先に実行し(これをプリプロセッシング機能という)、性別Mapオブジェクトのキー項目である「2」が取得される。その後、「*{getSexItems().get(‘2’)}」を実行し、「女」が表示されるようになっている。生年月日_月、生年月日_日も、性別と同じ仕組みで表示している。

サラリーマン型フリーランスSEという働き方でお金の不安を解消しよう先日、「サラリーマン型フリーランスSE」という働き方を紹介するYouTube動画を視聴しましたので、その内容をご紹介します。 「サ...

完了画面である「complete.html」の内容は以下の通り。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>完了画面</title>
</head>
<body>
   お申し込みが完了しました。<br/><br/>
    <input type="button" value="閉じる" onclick="window.close();" />
</body>
</html>

こちらは、完了メッセージと「閉じる」ボタンを表示している。

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-web-somescreen/demo

また、その他のフォームオブジェクトを利用した場合の実装方法は、下記サイトが参考になる。
https://qiita.com/rubytomato@github/items/8da1bb19537bbfc9c2ea

要点まとめ

  • 画面間で持ち回す各項目のformオブジェクトや、セレクトボックス・ラジオボタンで表示するリストは、Formクラス内で定義する。
  • formオブジェクトはコントローラクラスで生成し、「@ModelAttribute」アノテーションでHTMLファイルとの紐付けを行い、「@SessionAttributes」アノテーションでセッションとしてもたせされる。
  • HTMLファイルでのformオブジェクトの参照は「th:object」タグで行い、formオブジェクト内のメソッドへのアクセスは「th:field」タグで行う。
  • 式の一部に含まれる「__(変数)__」はプリプロセッシング機能で、式の中で先に実行される。