当社の新人エンジニア研修のWebアプリケーションとMVCパターン

当社の新人エンジニア研修のWebアプリケーションでは、MVCパターンを理解することを当面のゴールとしています。なぜなら、皆さんが実務につくと使用するであろう各種フレームワークがMVCパターンを考慮して作られているからです。

フレームワークの例としては、JavaのSpring Framework、C#のASP.NET Core MVC、PythonのDjango、Ruby on Railsなどが有名です。

この章では、MVCパターンの概略を紹介して、この後の章のガイド的な役割を果たします。

1. MVCパターンとは?

WebアプリケーションにおけるMVCパターンとは、主に以下の3つの要素から構成されるパターンです。

  • Model:アプリケーションのビジネスロジックやデータを表す部分(通常のJavaクラスで実装)
  • View:入出力(ユーザー画面)を担当する部分(Spring BootではThymeleafなどのテンプレートエンジンを使ったHTMLファイル)
  • Controller:ユーザーからのリクエストを受け取り、処理(Model)に振り分けて、処理結果をViewに返す部分(Spring Bootでは@Controllerアノテーションを付けたクラスで構成)

これらの頭文字を並べてMVCパターンと呼ばれています。MVCパターンはデザインパターンの一種で、過去のエンジニアが解決してきた設計のベストプラクティスがまとめられています。あとでDAO【Data Access Object】パターンなども学ぶ予定です。

データの流れ

ブラウザからのリクエストが、Spring Bootアプリケーション内のControllerクラスに送られます。

②ControllerはメソッドでModelのインスタンスを生成し、ビジネスロジックを実行したり、データベースアクセスを行ったりして処理を依頼します。

③Controllerは処理結果のデータをViewに渡します。

ViewがControllerから渡されたデータを利用してHTMLをレンダリング(画面に表示できるように)し、最終的にブラウザへレスポンスとして返します。

MVCパターンの概念を図示すると、以下のようになります。(イメージ図)

MVCパターンの概念図


MVCモデル

MVCパターンはMVCモデルと呼ばれることも多いのですが、「Mのモデル」と「MVC“モデル”」と後述の「Modelクラス」との混同を避けるため本書ではMVCパターンで統一しています。

2. なぜMVCパターンなのか?

MVCパターンが考案された理由を一言で説明すると、画面とロジックを別々に開発するためです。

皆さんが目にするWebアプリケーションは、画面(UI)がとてもオシャレで洗練されていると思います。それもそのはず、それらの画面はプロのデザイナーが作っているからです。

一方、そのWebアプリケーションの機能を支えるロジック(プログラム)はプロのITエンジニアが作っています。

デザインと機能、この両方を一人の人間が担当するのは困難です。また、画面(View)と処理(ModelとController)を分けることで、不具合発生時に原因を特定しやすくなり、システムの更新もしやすくなるのです。

そのような理由から、Webアプリケーションの主流はMVCパターンになっているわけです。

3. MVCパターンの簡単な例

ここからは、最も単純な例を使ってMVCパターンを紹介しましょう。
以下は、Spring Bootプロジェクトにおけるサンプル構成例です(講師の指示に従ってMVCの役割を埋めてみましょう):

my-mvc-sample (プロジェクト名)
 ┣ src
 ┃ ┣ main
 ┃ ┃ ┣ java
 ┃ ┃ ┃ ┣ com.example.demo
 ┃ ┃ ┃ ┃ ┣ DemoApplication.java  (役割:Spring Boot起動クラス)
 ┃ ┃ ┃ ┃ ┣ controller
 ┃ ┃ ┃ ┃ ┃ ┗ GameController.java (役割:     )
 ┃ ┃ ┃ ┃ ┣ model
 ┃ ┃ ┃ ┃ ┃ ┗ Kazuate.java        (役割:     )
 ┃ ┃ ┗ resources
 ┃ ┃ ┃ ┗ templates
 ┃ ┃ ┃    ┣ index.html           (役割:     )
 ┃ ┃ ┃    ┗ result.html          (役割:     )
 ┗ ...(テスト、設定ファイルなど)...

この中で、

  • Modelmodel パッケージの Kazuate.java
  • Viewtemplates フォルダ内の index.html および result.html
  • Controllercontroller パッケージの GameController.java

に対応します。

Spring Bootプロジェクトにおけるサンプル構成

3.1 プロジェクトを起動するためのクラス(例:DemoApplication.java)

以下のクラスは、Spring Bootアプリケーションを起動するための基本的な構成です。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}
  1. アノテーション @SpringBootApplication
    Spring Bootアプリケーションのエントリポイントとして必要な設定を自動で適用するアノテーションです。
  2. main メソッド
    Javaのプログラムの実行開始点で、SpringApplication.run() メソッドを呼び出すことでSpring Bootアプリケーションが起動します。

このクラスを右クリックしてSpring Bootアプリケーションとして実行すると組み込みサーバー(Tomcatなど)が立ち上がります。ただし、ブラウザのアドレスバーにhttp://localhost:8080/と入力してもindex.htmlなどを作成する前では以下のようなエラー画面が表示されることは前章で解説したとおりです。

404が表示されるのが正常

3.2 Modelクラス(例:Kazuate.java)

JavaSEの卒業課題などで作成したような数当てゲームのクラスがあるとします。かなり簡略化して以下のようなクラスです。

package com.example.demo.model;

import java.util.Random;

public class Kazuate {

	private int answer;
	private String message;

	public Kazuate() {
		Random random = new Random();
		this.answer = random.nextInt(10); // 0~9 の乱数
	}

	public void checkAnswer(int guess) {
		if (answer == guess) {
			this.message = "あたり";
		} else if (answer > guess) {
			this.message = "もっと大きいよ";
		} else {
			this.message = "もっと小さいよ";
		}
	}

	public int getAnswer() {
		return answer;
	}

	public String getMessage() {
		return message;
	}

	@Override
	public String toString() {
		return "デバック用 [answer=" + answer + ", message=" + message + "]";
	}
}
  • ①このクラス名はなんですか?
あなたの答え:
  1. ②フィールドはいくつあって、型は何で、名前は何ですか?
あなたの答え:
  • ③コンストラクタでは何をしていますか?
あなたの答え:
  • ④メソッドはいくつありますか? それぞれのメソッドの引数、戻り値の型、処理の内容を答えてください。
あなたの答え:
  • ⑤toString()メソッドの役割は何でしたか?
あなたの答え:

これらの質問に答えられない場合、「新人エンジニア研修向けJava解説」に戻って復習の必要があります。

3.3 動作テスト用クラス

以下のようなテストクラスで、コンソール上で動かしてみることもできます。

package com.example.demo.model;

import java.util.Scanner;

public class KTest {
	public static void main(String[] args) {

		System.out.println("0-9の整数で数を当ててください!");
		Scanner sc = new Scanner(System.in);
		int guess = sc.nextInt();
		sc.close();

		Kazuate k = new Kazuate();
		k.checkAnswer(guess);

		System.out.println(k.getMessage());
		System.out.println(k);

	}
}

コンソールでの挙動を確かめたうえで、これをWebアプリケーションとして動かす(移植する)イメージで進めます。

4. Controllerと画面(View)

これまでJavaSEのコンソールアプリとして動かしていたKazuateクラスを、Spring BootアプリケーションとしてWebブラウザで遊べるようにしてみましょう。

4.1 Controllerクラス

Spring Bootでは、以下のように@Controllerアノテーションと@GetMappingなどを使って、Controllerを実装します。MVCパターンでいえばControllerです。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.example.demo.model.Kazuate;

@Controller
public class GameController {

	// GET リクエストで /game にアクセスされた場合の処理
	@GetMapping("/game")
	public String playGame(String guess, Model model) {
		Kazuate kazuate = new Kazuate();
		
		int answer = Integer.parseInt(guess);
		kazuate.checkAnswer(answer);

		// モデルにオブジェクトを追加してビューに渡す
		model.addAttribute("k", kazuate);
		return "result"; // src/main/resources/templates/result.html を返す
	}
}

JavaSEの知識でこのクラスを読み解いてみましょう。

  • ①このクラスの名前は何ですか?
あなたの答え:
  • ②このクラスにフィールドとメソッドはそれぞれいくつありますか?
あなたの答え:
  • ③このクラスのメソッド名、仮引数、戻り値を答えなさい。
あなたの答え:
  • ④@Controllerのような@から始まる記述をなんと呼ぶのでしたか?
あなたの答え:

上記の質問に答えられない場合は、「新人エンジニア研修向けJava解説」に戻って復習の必要があります。

解説

  1. クラスの役割
    このクラスは、ユーザーのリクエストを受け取り、適切なビュー(HTMLページ)を返す役割を持つコントローラーです。@Controller アノテーションを付けることで、Spring Boot がこのクラスをWebアプリのコントローラーとして認識します。
  2. ゲームの処理
    1. @GetMapping("/game") により、/game にアクセスするとこのメソッドが実行されます。
    2. playGame(String guess, Model model)の第1引数はフォームから渡されるguessという名前の文字列データ、第2引数はモデルといってデータをHTMLに表示するためのクラスですが後述します。
    3. Integer.parseInt(guess)で文字列データを数値に変換します。
    4. Kazuate クラスの checkAnswer(answer) メソッドを使って、入力値が正解かどうかを判定します。
    5. model.addAttribute("k", kazuate); により、kazuate オブジェクトをビューに渡し、HTML側で結果を表示できるようにします。
    6. "result" を返すことで、src/main/resources/templates/result.html が表示されます。

4.2 入力画面(View)

下図のような入力画面を作ります。

ここでは例として5を入れている

以下はThymeleafを使ったHTMLテンプレートファイルの例です。(src/main/resources/templates/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>数当てゲーム</title>
</head>
<body>
    <h3>0-9の整数で数を当ててください!</h3>
    <form th:action="@{/game}" method="get">
        <input type="text" size="10" name="guess">
        <button type="submit">送信</button>
    </form>
</body>
</html>
  • ①index.htmlという名前のHTMLファイルには特別な役割がありました。それはなんでしたか?
あなたの答え:
  • ②このフォームのhttpメソッドはなんですか? また、このメソッドの特徴はなんでしたか?
あなたの答え:
  • ③このフォームの部品には何がいくつ使われていますか? また、リクエストパラメータは送信先で何という名前で扱えますか?
あなたの答え:

th:action="@{/game}"は、Thymeleafの書き方で、実行時に/gameというパスを指します。元々英語の【at】には場所を指定する役割(at the station のように)があります。(メールアドレスに@が使われるのもその意味です)

先頭に"/"がついているのはルートという意味です。

今回で言えば、「http://localhost:8080/」の最後の"/"がルートです。したがって実行時のパス(URL)は「http://localhost:8080/game」となります。

なお、th:という属性はThymeleafの先頭2文字から来ています。

method="get"と指定したので、ブラウザからGET /game?guess=5のようにリクエストが送られます(例:5を入力した場合)。

4.3 出力画面(View)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>結果画面</title>
</head>
<body>
    <h1>ゲームの結果は!</h1>
    <p th:text="${k.message}"></p>
    <br>
    <p th:text="${k}"></p>
</body>
</html>

  1. th:text="${k.message}" は、model.addAttribute("k", k) で渡された Kazuateオブジェクトから getMessage()を呼び出して表示します。メソッド名からgetが消えてMが小文字になっているルールに着目して下さい。プロパティアクセスするという重要な概念なので6章で後述します。
  2. th:text="${k}" は、toString()メソッドの結果を表示します。

Thymeleafが「HTMLテンプレートエンジン」と呼ばれる理由

テンプレートとは「ひな型」や「型紙」という意味です。あらかじめページの大まかな形を作っておき、動きのあるデータを後から組み合わせて、最終的なWebページを作るのがThymleafの仕組みです。

たとえば、商品の一覧ページを作る場合、一つひとつHTMLを書くのは大変ですが、テンプレートを使えば、同じ型を使い回しながら、表示するデータだけを変えることができます。これが、テンプレートと呼ばれる理由です。

例題 アドレスバーを使って数当てゲームを遊ぶ

  • アドレスバーのリクエストパラメータの数値(以下の5)を書き換えることで数当てゲームを遊ぶことができることを確かめなさい。
  1. result.htmlを直接ブラウザで開いてみた場合はどうなるか?
あなたの答え:

5. Spring Bootの起動・停止

Spring Bootアプリケーションは、DemoApplicationといったメインクラス@SpringBootApplicationを付与し、mainメソッドを実行することで内部的にTomcatサーバーが起動するのでした。

起動:複数の方法がありますが、一番のおすすめはBootダッシュボードから起動する方法です。最も簡単な方法で、Spring Bootアプリケーションの管理も楽になります。

  1. Eclipseのメニューで 「ウィンドウ」 → 「ビューの表示」 → 「その他」 → 「その他」 → 「Boot ダッシュボード」 を選択
  2. BootダッシュボードにSpring Bootプロジェクトが表示される
  3. 起動したいプロジェクトを選択して(再)起動や停止を選択
Boot ダッシュボード

6. JavaSEとの比較

研修内容がWebアプリケーションに入ると急にファイル数やフォルダ構成が増え、「なんだか複雑…」と感じるかもしれません。

しかし、本質はJavaSEで今までやってきた「入力(Input)、処理(Process)、出力(Output)」と同じです。

違うのは、コンソール上ではなくブラウザのHTMLに表示される点、ControllerやViewといったファイルごとの役割分担が明確になっている点です。

6.1 入力の比較

	System.out.println("0-9の整数で数を当ててください!");
	Scanner sc = new Scanner(System.in);
	int guess = sc.nextInt();
	sc.close();
   <h3>0-9の整数で数を当ててください!</h3>
    <form th:action="@{/game}" method="get">
        <input type="text" size="10" name="guess">
        <button type="submit">送信</button>
    </form>

6.2 処理の比較

	Kazuate k = new Kazuate();
	k.checkAnswer(guess);
	Kazuate kazuate = new Kazuate();
	kazuate.checkAnswer(answer);

	model.addAttribute("k", kazuate);
	return "result";

6.3 出力の比較

	System.out.println(k.getMessage());
	System.out.println(k);
   <p th:text="${k.message}"></p>
   <p th:text="${k}"></p>

JavaSEでは、Scannerで入力を受け取り、クラスのメソッドを呼んで処理し、System.out.println()で出力していました。

Spring Bootでは、<form>で入力を受け取り、Controllerメソッドで処理を呼び出し、Thymeleaf(HTML)で結果を表示します。


練習問題

練習問題1(MVCパターンの基礎理解)

次のMVCパターンにおける各役割に該当するものを、A〜Cから選んで答えてください。

役割

① Model(モデル)
② View(ビュー)
③ Controller(コントローラー)

選択肢

A)HTMLテンプレート(Thymeleaf)

B)ビジネスロジックやデータ処理を行うJavaクラス

C)ユーザーからのリクエストを受けて、Modelに処理を依頼し、結果をViewに渡すJavaクラス

あなたの答え:


練習問題2(MVCパターンでのデータの流れ)

Spring Bootを使ったWebアプリケーションで、ユーザーがフォームから数字を入力して送信するとします。この時の処理の流れを以下の選択肢(A〜D)から正しい順序で並べてください。

A)Controllerが受け取った入力値をもとにModelに処理を依頼する。

B)ユーザーがブラウザのフォームで数字を入力し送信する(リクエスト発生)。

C)Viewが受け取ったデータを使い、結果を画面に表示するHTMLを作成しブラウザに返す。

D)Modelがビジネスロジックを処理し、結果をControllerに返す。

あなたの答え: (   ) → (   ) → (   ) → (   )

練習問題3(MVCパターンを具体例で確認)

以下のSpring Bootプロジェクトのファイル構成を見て、質問に答えてください。

my-mvc-sample
 ┣ src
 ┃ ┣ main
 ┃ ┃ ┣ java
 ┃ ┃ ┃ ┣ com.example.demo
 ┃ ┃ ┃ ┃ ┣ DemoApplication.java
 ┃ ┃ ┃ ┃ ┣ controller
 ┃ ┃ ┃ ┃ ┃ ┗ GameController.java
 ┃ ┃ ┃ ┃ ┣ model
 ┃ ┃ ┃ ┃ ┃ ┗ Kazuate.java
 ┃ ┃ ┗ resources
 ┃ ┃ ┃ ┗ templates
 ┃ ┃ ┃    ┣ index.html
 ┃ ┃ ┃    ┗ result.html

次の各ファイルの役割をMVCの観点から説明してください。

① Kazuate.java はどの役割(M,V,C)に該当しますか?
あなたの答え:

② GameController.java はどの役割(M,V,C)に該当しますか?
あなたの答え:

③ index.html と result.html はどの役割(M,V,C)に該当しますか?
あなたの答え:

④ DemoApplication.javaの役割を簡単に説明してください。
あなたの答え

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□ MVCパターンを理解することが本研修のゴール

□ MVCパターンとは、通常のJavaクラスであるModel、入出力を担当するThymeLeafで作るView、ユーザーからの入力を受けて処理をModelに振り分けたり出力のViewに返したりするControllerの3つの部品で構成するパターンである

□ MVCパターンが考案された理由は画面とロジックを別々に開発するためである

□ JavaSEで利用・作成してきたクラスもおおむねModelとして使用できる

第2章の今回はMVCパターンについて学びました。ここでこのあとの章の全体像を示すことにします。JavaSEを学んできたみなさんがJavaWebを学ぶ際に最初に面食らうのがHTMLとJava間のデータの受け渡しです。そのため3章と4章でその部分をまずは学びましょう。

第3章は「コントローラーの基本 〜 データをWebに表示するまで」です。

このあとの章の全体像