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

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

この章では、 MVCモデル の概略を紹介して、この後の章のガイド的な役割を果たします。

1. MVCモデルとは?

WebアプリケーションにおけるMVCモデルとは

  • 通常のJavaクラスで作るModel
  • 入出力を担当するJSPで作るView
  • ユーザーからの入力を受けて処理をModelに振り分けたり出力のViewに返したりするJavaServletのController

からなる部品で構成するモデルです。それぞれの頭文字を並べてMVCモデルと呼ばれています。


それぞれの部品間のデータの流れは以下のとおりです。

  • ブラウザからのリクエストで送られたデータはServletが受け取ります。 Servletはアプリケーションサーバー上で動くJavaプログラムです。
  • Servletは必要なJavaのクラスをインスタンス化し、処理を委ねます。
  • ServletはデータをJSPに送りJSPが出力画面を作ります。JSPは、【Java Server Pages】の略で、Servletと同じく アプリケーションサーバー上で動くJavaプログラムです。HTMLと組み合わさって出力画面を作ります。
  • 最後に、JSPがレスポンスとしてブラウザに返されて画面に表示されます。

MVCモデルの概念を下図2.1にまとめます。

図2.1 MVCモデルとは

少しややこしいのですが、MのモデルとMVC“モデル”のモデルでは意味が違います。誤解を避けるためかMVC“パターン”と呼ぶこともあります。 MVCモデル はデザインパターンと呼ばれるものの一種で、過去のエンジニアが解決してきた設計のベストプラクティスがまとめられているのです。あとでDAO【Data Access Object】パターンというものも学ぶ予定です。

2. なぜMVCモデルなのか?

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

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

一方、皆さんが使っているWebアプリケーションは機能が豊富だと思います。その機能を支えるロジックは皆さんと同じプロのITエンジニアが作っています。

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

そのような理由から、Webアプリケーションの主流はMVCモデルになっている訳です。

3. MVCモデルの簡単な例

ここからは、最も単純な例を使ってMVCモデルを紹介しましょう。

下図2.2のようなプロジェクト構成を元に解説します。

必要なデータは講師から受け取るか、解説のソースコードをコピーしてください。

この中のファイルではModelが①のKazuate.javaに、Viewが②のresult.jspに、Controllerが③のGameServlet.javaに対応しています。

図2.2 基本となるプロジェクト構成

上記のプロジェクト構成を先のMVCモデルに当てはめると下図2.3のようになります。

先のプロジェクトの構成のファイルがどこに書かれているか分かりますか?

図2.3 MVCモデルと各ファイルの関係

※この図ではブラウザ側にindex.jspが書かれているが、これは以前のレスポンスでサーバからブラウザに送られたファイルであることを表現している

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

MVCモデルでいえばModelです。

package model;

import java.util.Random;

public class Kazuate {

	private int answer;

	public Kazuate() {
		Random random = new Random();
		this.answer = random.nextInt(10);
	}

	public String checkTheAnswer(int guess) {

		if (answer == guess) {
			return "あたり";

		} else if (answer > guess) {
			return "もっと大きいよ";

		} else {
			return "もっと小さいよ";
		}
	}

	public int getTheAnswer() {
		return this.answer;
	}

	public void setTheAnswer(int answer) {
		this.answer = answer;
	}
}
  • このクラス名はなんですか?
あなたの答え:
  1. フィールドはいくつあって、型は何で、名前は何ですか?
あなたの答え:
  • コンストラクタでは何をしていますか?
あなたの答え:
  • メソッドはいくつありますか? それぞれのメソッドの引数、戻り値の型、処理の内容を答えてください。
あなたの答え:

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

以下のテストクラスを使って動作を確かめてみてください。

package model;

import java.util.Scanner;

public class KazuateTest {

	public static void main(String[] args) {

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

		Kazuate kazuate = new Kazuate();
		String message = kazuate.checkTheAnswer(guess);
		int answer = kazuate.getTheAnswer();

		System.out.println(message);
		System.out.println("答えは" + answer);

	}
}

ここでは、このKazuateクラスを使ってWebアプリケーションとしても遊べるようにしてみましょう。(このようにあるシステムを別の環境でも動くようにすることを「移植」するといいます)

Webアプリケーション特有の詳細な文法の説明は後の章に譲り、大きな流れに着目して解説します。

皆さんはHTMLを学びましたね。

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

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

ソースコードは以下のようになります。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
    <head>
       <meta charset="UTF-8">
        <title>index.html</title>
    </head>
    <body>
        <h3>0-9の整数で数を当ててください!</h3>
        <form method="get" action="${pageContext.request.contextPath}/Game">
            <input type ="text" size ="10" name ="guess">
            <button type="submit">送信</button>
        </form>
    </body>
</html>
  • ファイル名がindexとなっているhtml(やjsp)はウェルカムファイルといって特別な役割がありました。それは何でしたか?
あなたの答え:
  • このフォームのhttpメソッドはなんですか? また、このメソッドの特徴はなんでしたか?
あなたの答え:
  • このフォームの部品には何がいくつ使われていますか? また、リクエストパラメータは送信先で何という名前で扱えますか?
あなたの答え:
  • 送信ボタンを押した後のアドレスバーの表記はどうなっていますか?
あなたの答え:http://localhost:8080

method = "get" とありますからhttpプロトコルのgetリクエストを使っていますね。

また、/プロジェクト名/Gameにフォーム入力したデータを送っています。このときの${pageContext.request.contextPath}という記述はEL式というもので詳しくは後述します。コンテキストパスと言って「/プロジェクト名」つまり「/03_JavaWeb02MVC」をこの記述で取得することができます。

このようにルートからたどるパスの指定をルートパス指定というのだと私達はHTMLで学びました。

name ="guess"とありますからリクエストパラメータにはguess(推測)という名前を付けてデータを送っています。

したがって「送信」ボタンを押した後のURLは下図2.5のようになります。この時のプロジェクト名の前の“/”はHTMLで学んだ通りルートです。一方プロジェクト名の後ろの“/”はコンテキストルートといいます。

図2.5 ルートとコンテキストルート

先のKazuateTest.javaとの対比でいえば、このJavaWebアプリの入力の部分は下の2つのコードような対応関係になります。一方のguessが変数(パラメータ)であるのに対して、他方のguessはname(リクエストパラメータ)である点に注意してください。

Scanner sc = new Scanner(System.in);
int guess = sc.nextInt();
<form method="get" action="${pageContext.request.contextPath}/Game">
    <input type ="text" size ="10" name ="guess">
    <button type="submit">送信</button>
</form>

次に、 以下のGameServlet.javaを見てみます。

Servletは拡張子が「.java」となっていることからも明らかなように通常のJavaプログラムです。ただし、Webアプリケーションでの役割としてはJSP(やHTML)から受け取ったデータを処理して他のServlet(やJSP)に返す役割に限定されています。

MVCモデルでいえばControllerですね。

package controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import model.Kazuate;

@WebServlet(urlPatterns = { "/Game" })
public class GameServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String str = request.getParameter("guess");
		int guess = Integer.parseInt(str);

		Kazuate kazuate = new Kazuate();

		request.setAttribute("message", kazuate.checkTheAnswer(guess));
		request.setAttribute("answer", kazuate.getTheAnswer());

		request.getRequestDispatcher("/view/result.jsp").forward(request, response);

	}
}

Servlet特有の文法は無視して、JavaSEの知識でこのServletを読み解いてみましょう。

  • このクラスの名前は何ですか?
あなたの答え:
  • このクラスのスーパークラスの名前は何ですか?
あなたの答え:
  • このクラスにフィールドとメソッドはそれぞれいくつありますか?
あなたの答え:
  • このクラスのメソッド名、仮引数、戻り値を答えなさい。
あなたの答え:
  • このクラスのメソッドの後ろについているthrowsキーワードは何のためにありますか?
あなたの答え:

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

ここでは、全てのServletは HttpServlet クラスを継承したクラスであるという点を記憶してください。

ここからはServlet特有の文法の説明です。

  • urlPatternsというのは、このJavaクラス(Servletいう特殊なクラスです)がどのようなurlで呼び出せるかということを表しています。今回は、「 Game」という名前で呼ばれることを表しています。
  • doGetというのは、getリクエストでデータが送られた場合は、このメソッドが働くということを意味しています。
    他にdoPostメソッドがありますが詳しくは後述します。
  • request.getParameter("guess")という記述は、この一文でHTMLフォームから送られた guess という名前のついたデータという意味になります。"5"という文字列として受け取ったデータを数値に変換しています。この処理を忘れるとIDEが注意してくれます。無視して実行しても500エラーが出ます。(試してみましょう)
  • request.setAttribute("message", kazuate.checkTheAnswer(num))という記述は、同一リクエスト間で有効な request 属性という記憶領域に kazuate.checkTheAnswer(num) の返り値の文字列をmessage という名前で保存することを意味しています。
    MVCモデルでは関係するJSPとServletの間で情報を共有するためにはリクエスト属性を利用するのが一般的です。
    リクエスト属性にデータを保存することで他のページでデータ(今回はmessageの内容)を取り出すことができるようになるのです。
  • 最終行の部分はresult.jsp に出力を依頼するための記述になります。「/view/result.jsp」のようにこの研修中はコンテキストルートからのパスを書くこととします。なお、この処理が行われた後もURLの表示はサーブレットのままです。URLに拡張子を表示しないことで、Webサイトの背後の技術をユーザーや第三者から隠すことができます。これにはセキュリティ上の理由がありますので必要に応じて講師から説明します。

この部分を先のJavaSEのテストクラスと対比すると以下のようになります。

Kazuate kazuate = new Kazuate();
String message = kazuate.checkTheAnswer(guess);
int answer = kazuate.getTheAnswer();
String str = request.getParameter("guess");
int guess = Integer.parseInt(str);
Kazuate kazuate = new Kazuate();
request.setAttribute("message", kazuate.checkTheAnswer(guess)); 
request.setAttribute("answer", kazuate.getTheAnswer()); 
request.getRequestDispatcher("/view/result.jsp").forward(request, response);

MVCモデルでいえばModelがKazuateクラスでしたね。

例題1

result.jspを単体で実行してみましょう。何が表示されますか? そこから何が言えますか?

あなたの答え:

出力のイメージは下図2.6のとおりです。

<アウトプットイメージ>

図2.6 入力値の5に対してコンピュータの値が2の場合の表示例

この画面のソースコードであるresult.jspというファイルを見てみましょう。

MVCモデルでいえばViewでしたね。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<html>
<head>
<meta charset=UTF-8">
<title>result.jsp</title>
</head>
<body>
				<h1>ゲームの結果は!</h1>
				${requestScope.message}
				<br> 答えは${requestScope.answer}
</body>
</html>
  • 1行目の記述はHTMLファイルにもありましたか?
あなたの答え:

1行目の記述は<%@ %>で囲まれていますね。実はこの部分だけJavaプログラムなのです。この記述の意味はJSP(JavaServer Pages)であることのディレクティブ(指示文)です。ブラウザにはHTMLが返されること、エンコーディングがUTF-8であることを示しています。ただし、この記述は決まり文句なのでJSPには必ず書くということだけ知っていれば十分です。

ここでのポイントは${ }という書き方です。

この記述をEL【Expression Language】といいました。

先のServletで以下のようにリクエストスコープという保存領域にメッセージにはmessage、答えにはanswerという名前を付けていました。

request.setAttribute("message", kazuate.checkTheAnswer(guess));
request.setAttribute("answer", kazuate.getTheAnswer());

そのため同じ名前でJSP側でも呼び出すことが可能なのです。

出力をJavaSEとWebアプリで対比すると以下のようになります。

System.out.println(message);
System.out.println("答えは" + answer);
${requestScope.message}
<br> 答えは${requestScope.answer}

ここまで見てきた通り、JavaSEとJavaWebは、細かい記述を無視すれば概ね同じと言えるのではないでしょうか?

下図2.7に数当てゲームのJavaWebアプリを図示しておきますので本章の振り返りに使ってください。

図2.7 数当てゲームのイメージ

研修内容がWebアプリケーションに入ると急速に難しくなったと感じて脱落してしまう新人エンジニアの方がいらっしゃいます。本当は多少ややこしくなっただけで、本質は今までやってきたInput,Process,Outputなのですが。。。

ただ、Webアプリケーションの方ファイル数が増えがちな点、ファイルの置き場所がフォルダ(ディレクトリ)とJavaパッケージに分かれる点も慣れるまでは大変かと思います。

したがってこれまでのJavaSEとこれからのMVCモデルを使ったJavaWebアプリは何が同じで何が違うのか?その点に気をつけて学んでいただければと思います。

MVC(Model-View-Controller)モデルを使用してJava Webアプリケーションを構築する場合、各クラスの役割は以下のようになります。

Model(モデル)はアプリケーションのビジネスロジックやデータを担います。この部分には、データを表すJavaBeans(後述)やDAO(Data Access Object)クラス、およびビジネスロジックを担当するクラスが含まれます。

View(ビュー)はユーザーインターフェースを担当します。Java Webアプリケーションでは、これにはJSP(JavaServer Pages)、CSS、JavaScriptなどが使われます。ビューはモデルからデータを受け取り、ユーザーに表示する形式で整形します。

Controller(コントローラー)はユーザーの入力を受け取り、モデルを操作して、適切なビューをレンダリングします。この研修では、Servletsなどがこの役割を果たします。


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

□ MVCモデルを理解することが本研修のゴール

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

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

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

今回はMVCモデルについて学びました。

次回は、ServletからJSPにデータを渡して表示する方法を学んでいきます。

「研修のゴールはMVCモデルを理解することです」 最後までお読みいただきありがとうございます。