Print Friendly, PDF & Email

当社の新人エンジニア研修のWebアプリケーションでは、MVCモデルを理解することを当面のゴールとしています。

なぜなら、皆さんが実務につくと使用するフレームワークでは、そのほとんどがMVCモデルを考慮して作られているからです。

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

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

MVCモデルとは?

WebアプリケーションにおけるMVCモデルとは、通常のJavaクラス(やJavaBeans)であるModel、入出力を担当するHTMLやJSPで作るView、Viewからの入力を受けて処理をModelに振り分けたり出力のViewに返したりするControllerからなる部品で構成するモデルです。

それぞれの頭文字を並べてMVCモデルと呼ばれています。

MVCモデルの概念を図に表すと以下のようになります。

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

なぜMVCモデルなのか?

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

皆さんが目にするWebアプリケーションも画面はとてもオシャレで洗練されていると思います。

それもそのはず、それらの画面はデザイナーが作っているからです。

一方、皆さんが使っているWebアプリケーションは機能が豊富だと思います。

その機能を支えるロジックは皆さんと同じITエンジニアが作っています。

デザインとロジック、この両方を一人の人間が担当するのは困難です。

また、画面(View)と処理(ModelとController)を分けることで不具合時に原因を特定することが容易になったりシステムの更新がやりやすくなるのです。

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

MVCモデルの簡単な例

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

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

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

プロジェクトの構成

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

MVCモデルの例(この図ではブラウザ側にindex.htmlが書かれているが、これは以前のレスポンスでサーバからブラウザに送られたファイルであることを意味している)

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

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

かなり簡略化して以下のようなクラスです。

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

package p02;

import java.util.Random;

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;
    }
}

以下にテストクラスを追加しておきますので、動作を確かめてみてください。

package p02;

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();
        
        Kazuate kazuate = new Kazuate();      
        String message = kazuate.checkTheAnswer(guess);
        int answer = kazuate.getTheAnswer();

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

    }
}

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

詳細な説明は後の章に譲り、ことでは大きな流れに着目して解説します。

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

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

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

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

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

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

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>数当てゲーム</title>
    </head>
    <body>
        <h3>0-9の整数で数を当ててください!</h3>
        <form method="get" action="/03_JavaWebText/GameServlet">
            <input type ="text" size ="10" name ="guess">
            <input type ="submit" value ="送信">
        </form>
    </body>
</html>

ここでの注目点は2箇所です。

9行目でmethod = "get" とありますからget メソッドを使って/03_JavaWebText/GameServletにフォーム入力したデータを送っています。

10行目でフォームに入力したデータにはname ="guess"ということでguessという名前を付けてデータを送っています。

したがって「送信」ボタンを押した後のURLは以下のようになります。

先のテストクラスとの対比でいえば、この入力の部分は次のような対応関係になります。(特徴的な部分は太字にしています。以下同様)

JavaSEの場合の記述JavaWebアプリの場合の記述

Scanner sc = new Scanner(System.in);
int guess = sc.nextInt();
<form method=“get” action=“/03_JavaWebText/GameServlet”>
<input type =“text” size =“10” name =“guess”>
<input type =“submit” value =“送信">
</form>
入力部分のソースコード比較

次に、 GameServlet を見てみます。

サーブレットは拡張子が「.Java」となっていることからも明らかなようにJavaプログラムです。

ただし、「extends HttpServlet」とあるように HttpServlet クラスを継承したクラスです。

その役割としてはHTML(やJSP)から受け取ったデータを処理して他のサーブレットやJSPに返す役割をします。

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

ここでの注目点は、5つあります。

  • 10行目のurlPatterns = {"/GameServlet"}
  • 14行目のdoGet
  • 16行目のrequest.getParameter("guess")
  • 21行目のrequest.setAttribute("message", kazuate.checkTheAnswer(num))
  • 23行目のrequest.getRequestDispatcher("02MVC/result.jsp").forward(request, response)

です。

package p02;

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;

@WebServlet(name = "GameServlet", urlPatterns = {"/GameServlet"})
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("02MVC/result.jsp").forward(request, response);

    }
}

  • 10行目のurlPatternsというのは、このJavaクラス(サーブレットという特殊なクラスです)がどのようなurlで指定されるかということを表しています。
    今回は、「 GameServlet 」という名前で呼ばれることを表しています。
  • 14行目のdoGetというのは、getメソッドでデータが送られた場合は、このメソッドが働くということを意味しています。
    他にdoPostメソッドがありますが詳しくは後述します。
  • 16行目のrequest.getParameter("guess")という記述は、この一文でHTMLフォームから送られた guess という名前のついたデータという意味になります。
    上記のサーブレットではこの後に"5"という文字列として受け取ったデータを数値に変換しています。
    この処理を忘れるとIDEが注意してくれます。無視して実行しても500エラーが出ます。(試してみましょう)
  • 21行目のrequest.setAttribute("message", kazuate.checkTheAnswer(num))という記述は、Webページ間で有効な request スコープという記憶領域に kazuate.checkTheAnswer(num) の返り値の文字列をmessage という名前で保存することを意味しています。
    詳しくは後述しますが、Webページというものは本来1ページ、1ページが独立していてデータを引き継ぐ仕組みがありませんでした。
    このようにhttp通信の要求と応答がそれぞれ独立していて他の http通信にデータを引き継がないことをステートレス(statelessといいます。
    state=状態がless=(引き継げ)ないというわけですね。
  • 23行目の部分も本来ステートレスなhttp通信をあたかもステートフル (stateful)であるかのように扱うために必要な記述です。
    result.jsp に出力を依頼するための記述になります。

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

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

JavaSEの場合の記述JavaWebアプリの場合の記述

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("02MVC/result.jsp").forward(request, response);
処理部分のソースコード比較

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

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

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

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

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

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>数当てゲームの結果表示</title>
    </head>
    <body>
        <h1>ゲームの結果は!</h1>
        ${message}<br>
        答えは${answer}
    </body>
</html>

ここでのポイントは10行目、11行目の${}という書き方です。

この記述をEL式といいます。

先のサーブレットで以下のようにメッセージにmessage、答えにanswerという名前を付けていました。

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

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

ここまでの結果を一覧表にすると以下のようになります。

JavaSEの場合の記述JavaWebアプリの場合の記述

Scanner sc = new Scanner(System.in);
int guess = sc.nextInt();
<form method=“get” action=“/03_JavaWebText/GameServlet”>
<input type =“text” size =“10” name =“guess”>
<input type =“submit” value =“送信">
</form>

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("02MVC/result.jsp").forward(request, response);

System.out.println(message);
System.out.println("答えは" + answer);
${message}<br>
 答えは${answer}
JavaSEとの比較

※厳密には以下の2行は入力とも考えられる。

String str = request.getParameter("guess");
int guess = Integer.parseInt(str);

Webアプリケーションの入ると急速に難しくなったと感じて脱落してしまう新人エンジニアの方がいらっしゃいます。

本当は多少ややこしくなっただけで、本質は今までやってきたInput,Process,Outputなのですが。。。

ただ、本来ステートレスとして作られたhttp通信を使ってオンラインで商取引などに使おうとしているわけですので、かなり無理をしている(?)感じはあるかと思います。

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

MVCモデルを使わない例