サーブレットとjspのおさらい

プログラミングを始める前に少しだけサーブレット (servlet)とjsp (Java Server Pages)についておさらいしておこうと思います。

サーブレットについて

サーブレットはJavaでWebアプリケーションを作成する際に使用される枠組みとして、1997年に導入されました。

Webアプリケーションを作るにはまず、ネットワークポートを開いてクライアントからの要求を待ち受け、クライアントからの要求を受け取ったらその内容に従ってアプリケーションロジックを実行し、必要な応答、通常はHTMLドキュメントを生成してクライアントに返信するという手順が必要となります。

ネットワークに関連するポートの待ち受けや応答の送信や要求や応答の提携部分の処理などの共通処理をアプリケーションロジックから切り離して、Webアプリケーションの開発者はアプリケーションロジックだけに注力すればいい仕組みとして、サーブレットは作られました。

そして共通処理部分はApache Tomcatのようなサーブレットコンテナが実行するようになっています。

サーブレットコンテナの動作

まず、サーブレットコンテナの動作について考えていきます。

HTTP要求はざっくりと3種類の情報で構成されています。まず処理の種類を示すメソッドと処理先を示すパスで構成される要求行、要求に関連した情報を伝える要求ヘッダ、要求内容を示す要求内容の3種類です。

サーブレットコンテナはパスから処理を委譲するサーブレットオブジェクトを特定して、メソッドに対応したオブジェクトメソッドを呼び出します。

HTTPのメソッドにはいくつかありますが、通常はリンクのクリックに対応するGETメソッドとフォームボタンのクリックで多く使用されるPOSTメソッドだけにアプリケーションロジックが対応すれば十分です。

パスに基づいたサーブレットオブジェクトの選択は基本的には2段階で行われます。

まず、パスの1段階目で複数のWebアプリケーションから一つのWebアプリケーションが (ディレクトリ名に基づいて) 選択され、2段階目でWebアプリケーション内のサーブレットオブジェクトから対応するものが選択されます。

サーブレットオブジェクトの選択は以前はweb.xmlというファイルにパス文字列とサーブレットオブジェクトのクラス名を記載しておき、サーブレットコンテナがこのファイルの内容に従ってサーブレットオブジェクトを選択する形式となっていました。

今はサーブレットオブジェクトのクラス定義にアノテーションでパス文字列を記載できるようになったので、web.xmlへの記載は必要なくなりました。多分、Webアプリケーションをインストールする際にアノテーションの内容をサーブレットコンテナが検査しているのだろうと思われますが、実際の動作は確認できていません。

サーブレットコンテナはWebアプリケーションに対するHTTP要求を処理して、要求ヘッダの内容とパスに含まれる、あるいは要求内容に記述されたパラメータからHttpServletRequestオブジェクトを生成し、必要な初期化を行ったHttpServletResponseオブジェクトと合わせてHTTPメソッドに対応したサーブレットオブジェクトのメソッドを呼び出します。

サーブレットオブジェクトはアプリケーションロジックにしたがって処理を行い、HttpServletResponseオブジェクトに必要な値を指定するとともにHttpServletResponseから取得したOutputStreamに結果、多くの場合はHTMLドキュメントを出力します。

とりあえずということで名前を受け取って挨拶を返すサーブレットを作ってみます。

まず、名前を入力するHTMLドキュメント"servlet_test.html"を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>サーブレットのテスト</title>
</head>
<body>
	<h1>サーブレットのテスト</h1>
	<hr>
	<h2>helloServlet</h2>
	<form action="helloServlet" method="post">
		<label for="name">名前を入力してください</label>
		<input type="text" id="name" name="name">
		<button>実行</button>
	</form>
</body>
</html>

見た通りの簡単なHTMLドキュメントで入力された名前を"name"パラメータに設定して"helloServlet"に"post"要求を出しています。

次にサーブレットを作成します。

package servlettest;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class helloServlet
 */
public class helloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public helloServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String name = request.getParameter("name");
		PrintWriter writer = response.getWriter();
		writer.println("<!DOCTYPE html>");
		writer.println("<html lang='ja'>");
		writer.println("<head>");
		writer.println("<meta charset='UTF-8'>");
		writer.println("<title>helloServlet</title>");
		writer.println("</head>");
		writer.println("<body>");
		writer.println("<h1>helloServlet</h1>");
		writer.println("<p>こんにちは、" + name + "さん!</p>");
		writer.println("</body>");
		writer.println("</html>");
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

手を抜いて作ったことがバレバレですが、eclipseのサーブレットテンプレートを使って、"name"パラメータの読み取りとHTMLドキュメントの出力を書いただけです。

先ほどのHTMLドキュメントではHTTPメソッドとして"post"を指定していました。サーブレットコンテナは"post" HTTPメソッドを受け取るとサーブレットオブジェクトの"doPost"メソッドを呼ぶのですが、eclipseのサーブレットテンプレートは既定で"doPost"メソッドが呼ばれると"doGet"メソッドを呼ぶようになっているので、"doGet"メソッドにアプリケーションロジックを書いています。

まあ、アプリケーションロジックといっても引数で渡されたHttpServletRequestオブジェクトのgetParameterメソッドを使用してパラメータを読み取り、HttpServletResponseオブジェクトのgetWriterメソッドで取得したPrintWriterにHTMLドキュメントを出力しているだけですけど。

プログラムとしてはこれで正しく動作するのですが、実運用の面では若干の問題があります。それはプログラムコードと出力されるHTMLドキュメントが分離されていないので、HTMLドキュメントの修正が面倒であること、プログラマーとは別に専任のデザイナーがHTMLドキュメントのデザインを管理するといった場合には修正が面倒になるといった問題があります。

こういった問題を避けるための方法として導入されたのがJava Server Pages (JSP) です。

Java Server Pages

サーブレットがJavaプログラム中でHTMLドキュメントを生成する形式をとっているのに対して、Java Server Pages (JSP) はHTMLドキュメント中にJavaプログラムを埋め込むことでHTMLファイルの修正を楽にしようというものです。

もちろんクライアントのWebブラウザがJavaプログラムを実行することはできない (昔はappletというJavaプログラムをブラウザ上で実行する仕組みがありましたが、様々問題があったので今は使われなくなっています) ので、サーブレットコンテナがJSPファイルを読み取って対応するサーブレットを自動的に生成し、JSPファイルへのアクセスがあれば対応するサーブレットのメソッド呼び出しを行うという形で実行されます。

まず、呼び出す側のHTMLドキュメントにhelloJsp.jspへのアクセスを追加します。

<!DOCTYPE html>
<html lang="ja">

<head>
	<meta charset="UTF-8">
	<title>サーブレットのテスト</title>
</head>

<body>
	<h1>サーブレットのテスト</h1>
	<hr>
	<h2>helloServlet</h2>
	<form action="helloServlet" method="post">
		<label for="name1">名前を入力してください</label>
		<input type="text" id="name1" name="name">
		<button>実行</button>
	</form>
	<hr>
	<h2>helloJsp</h2>
	<form action="helloJsp.jsp" method="post">
		<label for="name2">名前を入力してください</label>
		<input type="text" id="name2" name="name">
		<button>実行</button>
	</form>
</body>

</html>

先ほどの例をJSPで作ってみます。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>helloJsp</title>
</head>
<body>
<h1>helloJsp</h1>
<p>こんにちは、<%= request.getParameter("name") %>さん!</p>
</body>
</html>

先頭の行はJSPファイルであることを示す決まり文句です。そして "<%=” と "%>" で囲まれた範囲がJavaプログラム (と言っても単にパラメータを読みだすだけですけど) です。

このようにJSPではHTMLドキュメント中の結果を出力したい位置に結果を出力するJavaコードを書かなければならないので、多数の出力がある場合などにはJavaコードが分散してしまう、複雑なアプリケーションロジックが含まれる場合にはJavaコードが大きくなり、HTMLとして編集しにくくなるといった問題があります。

このため、通常はJSPファイル単体ではなくサーブレットとJSPとを組み合わせて使います。

まず、サーブレットで要求を受け取り、アプリケーションロジックで必要な処理を行った後にJSPファイルで結果を埋め込んだHTMLドキュメントを作成するという実装になります。

このようなサーブレットとJSPの連携にはフォワードという機能を使用します。フォワード機能はクライアントに通知せずにパスで指定したサーブレット、JSPなどを連続して実行するもので、HttpServletRequest や HttpServletResponse などを指定したサーブレットやJSPなどに引き継ぐことができます。

実際にフォワード機能を使用した例を見ていきます。まずは例によって呼び出す側のHTMLドキュメントにhelloServletJspの呼び出しを追加します。

<!DOCTYPE html>
<html lang="ja">

<head>
	<meta charset="UTF-8">
	<title>サーブレットのテスト</title>
</head>

<body>
	<h1>サーブレットのテスト</h1>
	<hr>
	<h2>helloServlet</h2>
	<form action="helloServlet" method="post">
		<label for="name1">名前を入力してください</label>
		<input type="text" id="name1" name="name">
		<button>実行</button>
	</form>
	<hr>
	<h2>helloJsp</h2>
	<form action="helloJsp.jsp" method="post">
		<label for="name2">名前を入力してください</label>
		<input type="text" id="name2" name="name">
		<button>実行</button>
	</form>
	<hr>
	<h2>helloServletJsp</h2>
	<form action="helloServletJsp" method="post">
		<label for="name3">名前を入力してください</label>
		<input type="text" id="name3" name="name">
		<button>実行</button>
	</form>
</body>

</html>

次にこの要求を受け取るサーブレットを定義します。

package servlettest;

import java.io.IOException;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class helloServlet
 */
public class helloServletJsp extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public helloServletJsp() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String name = request.getParameter("name");
		request.setAttribute("greet_to", name);
		RequestDispatcher dispatcher = request.getRequestDispatcher("helloServletJsp.jsp");
		dispatcher.forward(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

doGetメソッドがだいぶ簡潔になっています。まず、request.getParameter("name") でnameパラメータの値を読みだした後、request.setAttribute("greet_to", name) でその値を greet_to 属性に保存しています。

続いて request.getRequestDispatcher("helloServletJsp.jsp") で helloServletJsp.jsp JSPファイルにフォワードするためのリクエストディスパッチャーを取得して、dispatcher.forward(request, response) で JSP ファイルにフォワードしています。この時 HttpServletRequest と HttpServletResponse もそのまま渡していますので、request.setAttribute メソッドで greet_to 属性に保存した name パラメータの値も JSP ファイルに渡されます。

最後にhelloServletJsp.jsp JSP ファイルを見ておきます。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>helloServletJsp</title>
</head>
<body>
<h1>helloServletJsp</h1>
<p>こんにちは、${greet_to}さん!</p>
</body>
</html>

${greet_to} にて greet_to 属性に保持されている値を表示しています。なお、$(greet_to} は <@= request.getAttribute("greet_to")> の省略形のようなもの (詳しくはEL式を調べてみてください) です。

ということでサーブレットとJSP (Java Server Pages) についてザックリとみてきました。

次は実際のコーディングに入りたいと思います。


この記事が気に入ったらサポートをしてみませんか?