サーブレット

サーブレットとは

今回は、サーブレット(Servlet)について説明します。サーブレットとは、サーバ上でウェブページなどを動的に生成したりデータ処理を行うために、Javaで作成されたプログラムのことです。

JSPが、HTML内にプログラムを埋め込んでいたのに対し、サーブレットは、HTMLを生成できるJavaプログラムと考えると分かりやすいでしょう。基本的に両者は同じもので、JSPも最終的には内部的にサーブレットに変換されて出力されるのです。

サーブレットとJSPの使い分け

ただ、それぞれの主な使用目的には違いがあります。それは、JSPが、Webアプリの表示部分に特化しているのに対し、サーブレットは処理の部分に特化することを目的としている点にあります。

たとえば、情報を検索するサイトが存在するとします。その時、検索ワードを入力するページはJSPで記述し、そのワードをもとに検索を行う処理を行うのがサーブレット、そしてその結果を表示するのが、JSP…といった使い方をすると、非常に効率的にアプリを作ることができます。

簡単なサーブレットの例

サンプルプログラム

では早速、簡単なサーブレットの例を見てみましょう。まずは、サーブレットを用いて簡単なHTMLを出力するケースを見てみることにします。以下のサンプルを実行してみてください。

なお、サーブレットの作り方については、開発環境としてEclipseを使用する場合には、こちらを参考にして下さい。その際、パッケージ名をday6、クラス名をSample01としてください。

Sample01.java
package day6;

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

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

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out = response.getWriter();

		out.println("<html>");
		out.println("<head>");
		out.println("<title>SampleServlet</title>");
		out.println("</head>");
		out.println("<body>");
		out.println("<h1>HelloServlet</h1>");
		out.println("</body>");
		out.println("</html>");
	}
}

実行結果は、以下のようになります。(図6-1.)出力結果のURLは、http://localhost:8080/jsp/day6/Sample01となります。

図6-1.単純なサーブレットの実行結果
単純なサーブレットの実行結果

プログラムの内容

3行目~8ぎょうめで、必要なクラスをインポートしています。

Sample602.sql
import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

これらは、暗黙オブジェクトを利用する際には必要となるものです。JSPではもともとインポートされていましたが、サーブレットではインポートする必要があります。ただ、Eclipseを使用している場合は、サーブレットのコードを自動生成する際には、この部分が自動的に生成されるので、そのまま利用すると良いでしょう。

doGetとdoPost

プログラムを見ればわかる通り、このサンプルには、通常のJavaのコンソールアプリにあるような、main()などがありません。では、どこに処理を記述するのでしょうか?それが、27行目に出ている、doGetメソッドです。

doGetメソッドは、外部から、GET送信が送られてきた時に実行されるメソッドです。同様に、POST送信が行われてきた時の処理を実行するメソッドとして、doPostがあります。(図6-2.)ここでは、doGetの身を使っています。このサンプルのように、単純にURLを指定し実行するタイプであれば、doGetでよいでしょう。

図6-2.doGetとdoPost
doGetとdoPost

requestとresponse

次に、doGetメソッドの中を見てみましょう。ここには、サーブレットの記述を行う上で非常に大事になる、requestresponseが引数として渡されます。これらは、暗黙オブジェクトで、JSPでも使えますが、サーブレットでは特に大事になります。

Webブラウザからの要求を処理するのがrequestであり、Webサーバからの応答を処理するためのがresponseとなります。ここでは、requestで、リクエストのキャラクターエンコードをutf-8に(30行目)、responseで出力するHTMLのエンコードをを同じくutf-8(31行目)にしています。

requestおよびresponseのエンコードをutf-8に設定
request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");

HTMLの出力

では、このプログラムでは、どのようにしてHTMLを出力しているのでしょう。サーブレットはJSPと違い、暗黙オブジェクトはありません。なので、出力するためのオブジェクトをresponseのgetWriter()メソッドを使用して呼び出しています。

あとは、JSPの場合と同様に、「out.print("<html>")」のようにすると、HTMLとして文字列が出力されます。

outオブジェクトの取得
PrintWriter out = response.getWriter();

PrintWriterクラスは、print()メソッドやprintln()メソッド、更にprintf()メソッドとformat()メソッドも実装しているクラスで、コンソールに文字を出力する「System.out」のようにして様々なターゲットに文字列を出力できるためのクラスです。

以上が、このサンプルのプログラムの流れです。

URLの決定

ところで、サーブレットには、JSPとは決定的に違う大きな問題があります。それは、「いかにして結果のURLを決めるのか」という問題です。JSPの場合は、作成したプロジェクトのどの位置に配置されるかで、URLが自然に決まりましたが、サーブレットの場合はそうはいきません。では、どうすればよいのでしょうか?

このサンプルの場合、それを決めているのが13行目のアノテーションです。

URLを決めるアノテーション
@WebServlet("/day6/Sample01")

このプロジェクトは、jspとして話を進めてきましたから、URLは、http://localhost:8080/jsp/の部分までは確定しています。そのあとに続く部分を決めているのが、この@WebServletアノテーションです。

従来web.xmlをいうファイルにURL文字列を対応付けるための記述を追記する必要があったのですが、このアノテーション@WebServlet(URL文字列)とすることによりweb.xmlの修正無く自動で対応付けをしてくれるための記述となります。

doGetとdoPost

doGetとdoPostの使い分け

すでに説明した通り、サーブレットの外部からリクエストがあった場合、GET送信であればdoGetメソッドが、POST送信であれば、doPostメソッドが呼び出されます。次は、実際にこの2つを使い分けるサンプルを見てみることにしましょう。以下のサンプルを実行してみてください。

Sample02.java
package day6;

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

/**
 * Servlet implementation class Sample02
 */
@WebServlet("/day6/Sample02")
public class Sample02 extends HttpServlet {
	private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Sample02() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
		throws ServletException, IOException {
		// TODO Auto-generated method stub
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out = response.getWriter();

		out.println(output("Get"));

	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
		throws ServletException, IOException {
		// TODO Auto-generated method stub
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out = response.getWriter();

		out.println(output("Post"));

	}
	public StringBuffer output(String type){
		StringBuffer sb = new StringBuffer();

		sb.append("<html>");
		sb.append("<head>");
		sb.append("<title>Sample02</title>");
		sb.append("</head>");
		sb.append("<body>");

		sb.append("<p>呼び出し方法:" + type + "送信</p>");

		sb.append("<a href='Sample02'>リンク</a>");

		sb.append("<form action='Sample02' method='get'>");
		sb.append("<input type='submit' value='GET送信' />");
		sb.append("</form>");

		sb.append("<form action='Sample02' method='post'>");
		sb.append("<input type='submit' value='POST送信' />");
		sb.append("</form>");

		sb.append("</body>");
		sb.append("</html>");

		return (sb);
	}
}

実行結果は、以下のようになります。(図6-3.)出力結果のURLは、http://localhost:8080/jsp/day6/Sample02となります。このサンプルは、送られてきたリクエストがGET送信か、POST送信かを区別して、それを出力するというものです。

図6-3.Sample02の実行結果
Sample02の実行結果

実行時は、起動時はGET送信であるため「GET送信」と表示されます。ここで「POST送信」ボタンを押してみてると、「POST送信」であると出力されます。(図6-4.)

図6-4.「POST送信」ボタンを押した場合の処理
「POST送信」ボタンを押した場合の処理

ここで、「GET送信」を押せば、ふたたび表示は「GET送信」になります。また、リンクをクリックした場合も「GET送信」と出ます。

プログラムの仕組み

このサンプルの仕組みは、以下の通りです。

doGetメソッドとdoPostメソッドを作成し、それぞれrequestとresponseの文字コードとoutオブジェクトの生成をしています。最後に表示する時にoutputメソッド呼び出しをして引数にgetで呼び出された場合Get、postで呼び出された場合Postという文字を渡しています。

output(String type)で、前ページのgetもしくはpostを受け取り、その引数を使用して文字を出力します。また、<a>タグでのget送信、<form>タグでのGET送信とPOST送信の3つの記述をしています。

以上から、どのような状況でdoGet、doPostメソッドが呼び出されるかがわかると思います。

web.xml

最後に、Sample01の説明でのべた、web.xmlについて説明したいと思います。

本来URLパターン文字列とサーブレットを関連付ける必要がありまず。その設定ファイルがweb.xmlです。格納場所は、<プロジェクト名(コンテキスト)>\WebContent\WEB-INF\web.xmlです。

サーブレットはweb.xmlに作成されたクラスファイルの完全修飾名と、定義されたurl-pattern(URL文字列)にマッピング(呼び出す場合のURLの対応付け)をしなければいけません。

つまり、指定したプログラムを実行するために、指定されたURL文字列でリクエストさせる」という設定をweb.xmだというわけです。では、実際にSample01をweb.xmlに関連付けた場合のサンプルを見てみることにしましょう。

web.xmlの記入例
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
  <display-name>jsp</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
	<servlet>
        <servlet-name>Sample01</servlet-name>
        <servlet-class>day6.Sample01</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Sample01</servlet-name>
        <url-pattern>/day6/Sample01</url-pattern>
    </servlet-mapping>
</web-app>

ここで着目する必要があるのは、<servlet>タグ、並びに<servlet-mapping>タグで囲まれた部分です。

<servlet-name>はそのまま他と区別する名前を記述しています。<servlet>要素の<servlet-class>は実際に実行するクラスファイルの完全修飾されたクラス名を記述します。

<servlet-mapping>要素の<url-pattern>は、アプリケーションコンテキストからの『/』以降を記述します。http//localhost:8080/jsp/day6/Sample01であれば、アプリケーションコンテキストはjspなのでその「/」以降の/day6/Sample01が記述されています。

すでに説明したように、現在は実際にはweb.xmlを操作することはありませんが、Javaのバージョンによってはweb.xmlが必要になることが必要になるので、注意が必要です。