連載
» 2004年03月18日 00時00分 公開

基礎から学ぶサーブレット/JSP(11):JSPとサーブレットの違いを明らかにする (1/2)

[山田祥寛,@IT]

 これまで10回にわたって、JSP(Java Server Pages)をベースとしたコーディングの手法について学習してきました。ここまで順に読み進めてこられた方ならば、JSP開発が手軽でありながら、いかに強力なアーキテクチャを提供しているのか、きっとお分かりになられたはずです。

 しかし、それならば「すべてのサーバサイドアプリケーションはJSPで記述すべき」なのでしょうか。答えははっきりと「否」です。

 第1回「サーブレット/JSPの役割を理解する」でも紹介したように、JSPとは決してサーブレットの「後継」技術ではありません。サーブレットの問題点を解決すべく登場した技術であることは間違いありませんが、これによって「JSPにサーブレットが置き換わる」というものではなく、「JSPとサーブレットと、それぞれの得意分野に応じて、技術を使い分ける選択肢が与えられた」と考えるのが正しいとらえ方といえましょう。

 ここではもはやサーブレットとJSPとの使い分けについて再掲することはしません(内容について忘れてしまったという方は、いま一度、第1回を参照してみてください)。が、1つだけ押さえておいていただきたいのは、「ある一定以上の規模のアプリケーションを構築する場合には、ただ単に目前のコーディングの手軽さだけではなく、アプリケーションに変更があった場合のメンテナンス性にも思いを至らせる必要がある」ということです。その1つのアプローチとして、JSPとサーブレットの使い分けがあると思っていただければよいでしょう。

サーブレットクラス記述・実行の「3つの条件」

 さて、それでは早速、具体的なサーブレットクラスを記述してみることにします。以下は、第10回「クラスライブラリを攻略「データベース編」」のaccessLog.jspをサーブレット化したものです。

AccessLog.java
package to.msn.wings.atmarkit;

import java.io.*;
import java.sql.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;


public class AccessLog extends HttpServlet {
  public void doGet(HttpServletRequest request,HttpServletResponse response)
    throws ServletException,IOException {
    Connection db=null;
    PreparedStatement objSql=null;
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
      db=DriverManager.getConnection("
           jdbc:mysql://localhost/sample?user=root&password=root&useUnicode=
true&characterEncoding=Shift_JIS");
      objSql=db.prepareStatement("
        INSERT INTO log_rec(url,atime,usr,referer) VALUES(?,?,?,?)");
      SimpleDateFormat objFmt=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      objSql.setString(1,request.getServletPath());
      objSql.setString(2,objFmt.format(Calendar.getInstance().getTime()));
      objSql.setString(3,request.getHeader("user-agent"));
      objSql.setString(4,request.getHeader("referer"));
      objSql.executeUpdate();
      objSql.close();
      db.close();
    } catch (Exception e) {
      throw new ServletException(e);
    } finally {
      if(objSql!=null){
        try {
          objSql.close();
        } catch(SQLException e) {
          throw new ServletException(e);
        }
      }
      if(db!=null){
        try {
          db.close();
        } catch(SQLException e) {
          throw new ServletException(e);
        }
      }
    }
  }
}

 HTMLをベースとしたJSPと異なり、生々しいJavaのコードがむき出しになっているサーブレットクラスは、一見、とても原始的にも見え、初心者の方にはとっつきにくく思われる一因となっています。

 しかし、その実、ポイントさえ押さえてしまえば、JSPとサーブレットで記述している内容は「ほとんど変わりありません」。JSPが隠ぺいしていた泥臭い「構文規則」(それはまったく機械的に記述できる骨組みの部分です)さえ除いてしまえば、JSPとサーブレットのコードはほとんど等価であるといえるのです。

 そうした意味で、見掛けに惑わされて、JSPとはまったく別物の技術としてサーブレットの学習を始めるのは大変もったいないことです。皆さんには、せっかくここまで学習したJSPのノウハウがあるわけですから、本稿ではそこからの差分のみ――サーブレット特有の知識をJSPとの比較という観点でご紹介していこうと思います。

サーブレットクラスには3つのパッケージが必要

 JSPページ内で外部クラスを使用する場合には、あらかじめ@pageディレクティブのimport属性でパッケージ(またはクラスそのもの)をインポートしておく必要がありました。このimport属性と同等の機能を提供するのが、サーブレットクラスではimport命令となります。コードの先頭に記述する必要があります。

 さて、このimport命令、JSPにおいては意識する必要がありませんでしたが、サーブレットクラスではすべてのコードで「必ず」インポートしておかなければならないパッケージが3つあります。

  • java.ioパッケージ
  • javax.servletパッケージ
  • javax.http.servletパッケージ

 これら3つのパッケージは、JSPで不要であったというわけではありません。JSPではコンテナがサーブレットへ変換する際に自動的に補完してくれていたにすぎないのです。サーブレットには、このような自動補完のしくみはありませんので、自分自身で明示する必要があります。

 もちろん、そのほかに必要なパッケージ(クラス)が存在する場合には、適宜、import命令を併記します。import命令は、@pageディレクティブのimport属性のようにカンマ区切りでパッケージを指定することはできませんので、注意してください。必ず複数のパッケージは複数のimport命令でインポートします。

サーブレットクラスはHttpServletクラスを継承する

 javax.http.servlet.HttpServletクラスは、サーブレットクラスの基本的な機能(クライアントからの要求受信・応答送信・処理振り分けなど)を備えたクラスです。サーブレットクラスを定義する際には、必ずこのHttpServletクラスを継承する必要があります。HttpServletが汎用的なクライアント−サーバ間の通信機能を提供し、ユーザーはこのHttpServletクラスの機能を利用しながら、独自のロジックを組み込んでいくというイメージでとらえておけばよいでしょう。

 以上を踏まえて、以下にサーブレットクラスの最も基本的なスケルトンを示しておくことにします。

package パッケージ名;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;


public class クラス名 extends HttpServlet {
  public void doXxx(HttpServletRequest request,HttpServletResponse response)
    throws ServletException,IOException {
    ロジックの中身
  }
}

 package命令は、サーブレットクラスを再利用の単位にグルーピングしたい場合などに用います。一定規模以上のアプリケーションでは、クラス名の衝突を防ぐために、このパッケージという概念を用います(詳細は、「連載:いまから始めるJava 第10回「Javaのクラスをグループ化するパッケージ」」を参照)。クラスを定義する場合、パッケージの宣言は必ず行うようにしてください。

 サーブレットクラスの実際のロジックは、doXxxメソッドに記述します。サーブレットクラスがコールされたタイミングで、クライアントからのリクエストの方法によって、それぞれ適したメソッドが呼び出されることになります。つまり、サーブレットにおいては、「そのサーブレットがどのような方法で呼び出されるか」を意識して、コードを記述する必要があります。

HttpServletクラスにおける主なdoXxxメソッド
メソッド名 呼び出されるタイミング
doDelete DELETEリクエストを受けたタイミング
doGet GETリクエストを受けたタイミング
doHead HEADリクエストを受けたタイミング
doOptions OPTIONSリクエストを受けたタイミング
doPost POSTリクエストを受けたタイミング
doPut PUTリクエストを受けたタイミング
doTrace TRACEリクエストを受けたタイミング

 すべてのdoXxxメソッドは、パラメータとしてHttpServletRequestとHttpServletResponseオブジェクトとを受け取ります。これらはそれぞれJSPにおける暗黙オブジェクトrequest、responseに該当するもので、doXxxxxメソッド内で自由に使用することができます(ほかの暗黙オブジェクトは明示的に宣言する必要があります。後述)。

 HttpServletクラスには7種類のdoXxxxxメソッドが用意されていますが、一般的によく使用するのはdoGetとdoPostメソッドの2つです。ここでは、これら2つのメソッドがどのようなタイミングで呼び出されるのかを整理しておくことにしましょう。

(1)doGetメソッド

  • ブラウザから直接にURL指定で呼び出された場合
  • ページ内のリンク(<a>タグ)や<img>、<frame>タグなどを介して呼び出された場合
  • HTMLフォーム(<form>タグ)からGETメソッドでページが要求された場合(methodオプションはデフォルトで“GET”と見なされます)

(2)doPostメソッド

  • HTMLフォーム(<form>タグ)からPOSTメソッドでページが要求された場合

サーブレットクラスの起動にはweb.xmlへの登録が必須

 JSPページをコールするには、単純にアプリケーションルート配下の任意のフォルダ(ただし、「WEB-INF」フォルダを除く)に「.jsp」ファイルを配置し、ブラウザから対応するURLを指定すればよいだけでしたが、サーブレットの場合は「/WEB-INF/classes」フォルダ配下にコンパイル済みの「.class」ファイルとして配置し、かつ、web.xml(デプロイメントディスクリプタ)にサーブレットクラスを登録する必要があります。

 ここでは、冒頭のAccessLog.javaを、実際にアプリケーションに配置してみることにしましょう。

(1)サーブレットクラスをコンパイルする

 JSPと異なり、サーブレットクラス(「.java」ファイル)は配置に先立って、あらかじめコンパイル(一括翻訳)を行い、「.class」ファイルを生成しておく必要があります。コンパイルは一般的にJ2SEに付属しているコマンドラインツールjavacを使用して行ないますが、javacを使用する前にいくつかの環境変数を設定しておくと便利です。環境変数とは、環境上であらかじめ定められたパラメータ値のことで、アプリケーションやコマンドが実行される際などに参照されます。例えば、環境変数CLASSPATHであれば、Java仮想マシンがJavaアプリケーションを実行する際、あるいはコンパイラが「.java」ファイルのコンパイルを行う際などに、必要なクラスライブラリを検索するためのパスを設定します。(環境変数の設定は必須ではありませんが、環境変数の設定を行なわない場合、必要なパラメータを毎回コマンドで指定しなければならず、不便です)。以下には、本稿で推奨する最低限の環境変数を挙げておくことにします。

環境変数の設定値(Windows環境の場合)
環境変数名 設定値
PATH .;%JAVA_HOME%\bin
JAVA_HOME C:\j2sdk1.4.2_03
CATALINA_HOME C:\Program Files\Apache Software Foundation\Tomcat 5.0
CLASSPATH .;%CATALINA_HOME%\common\lib\servlet-api.jar;
%CATALINA_HOME%\common\lib\jsp-api.jar;
%CATALINA_HOME%\webapps\atmarkit\WEB-INF\classes;

 環境変数の値は、J2SEやTomcatのバージョンなどによって異なる可能性があるので、注意してください。また、Windowsの場合、バージョンによっては、環境変数を設定後、マシン自体を再起動する必要がありますので、注意してください。

 環境変数の設定が完了したら、コマンドプロンプトから以下のコマンドを入力してください。カレントフォルダはAccessLog.javaを配置してあるフォルダにあるものとします。

>javac AccessLog.java

 コンパイルに成功した場合には「AccessLog.class」が生成されるはずです。もしも万が一、コンパイルに失敗してしまったという場合には、いま一度、スペリングミスがないかどうかを確認してみると同時に、上記環境変数の設定が間違っていないか、マシンを再起動したかなどを再確認してみてください。

 to.msn.wings.atmarkitパッケージに属するサーブレットクラス(「.class」ファイル)は、「/WEB-INF/classes/to/msn/wings/atmarkit」フォルダに配置する必要があります(フォルダ構造がパッケージ階層に対応していなければなりません)。

(2)サーブレットクラスをweb.xmlに登録する

 さて、このように配置したサーブレットクラスを、デプロイメントディスクリプタに登録します。以下のように記述してみてください。

<?xml version="1.0" encoding="Shift_JIS" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"version="2.4">
  <servlet>
    <servlet-name>AccessLog</servlet-name>
    <servlet-class>to.msn.wings.atmarkit.AccessLog</servlet-class>
  </servlet>
  <servlet-mapping>
     <servlet-name>AccessLog</servlet-name>
     <url-pattern>/AccessLog</url-pattern>
   </servlet-mapping>
</web-app>

 <servlet>要素では、サーブレットの完全修飾名(パッケージ名を含むクラス名)に対応する論理名を定義します。論理名は自由に命名することが可能ですが、必ずデプロイメントディスクリプタ内で一意になるように設定してください。

 この論理名に対して、実際にサーブレットをコールするURLとのマッピングを行うのが、<servlet-mapping>要素の役割です。ここでは、論理名「AccessLog」で表されるサーブレットを「http://localhost:8080/atmarkit/AccessLog」で呼び出すことができるように定義しています。

 デプロイメントディスクリプタを変更した場合には、必ずコンテナ(Tomcat)を再起動してください。

注意:未登録サーブレット(“Invoker”Servlet)

Tomcat 4.0.12以前では、サーブレットをデプロイメントディスクリプタに登録せずに起動できる、「“Invoke”Servlet」というしくみが用意されていました。しかし、現在のバージョンではセキュリティ上の理由から“Invoker”Servletは推奨されていません。旧バージョンとの互換性という意味から、conf/web.xmlの設定を変更することで、“Invoker”Servletを利用すること「も」可能ですが、新しくサーブレットを作成する場合には必ずデプロイメントディスクリプタに登録するようにしてください。


One Point

サーブレットクラスには(1)java.io、javax.servlet、javax.http.servletの3パッケージをインポートする、(2)HttpServletクラスを継承する、(3)アプリケーションロジックはdoXxxxメソッドに記述する、などのルールがあります。また、配置に際しても、必ずデプロイメントディスクリプタへの登録が必要になります。


       1|2 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。