前回はMVCモデルについて概略を学びました。

今回はコントローラーの役目を果たす(ことが多い)サーブレットについて学んでいきましょう。

1. サーブレットとは?

Java Servletとは、Javaで書かれたプログラムであり、サーバ上で動きます。以前はJava Appletというクライアント(ブラウザ)側で動くJavaプログラムがありJava Servletと対になっていたためこの名前があります。(現在では Java Appletはほとんど使われていません)

サーブレットの概念は下図3.1の通りです。

ブラウザからアプリケーションサーバにリクエストが送られると初回のみServletコンテナがJavaのクラスファイルを実行してサーブレットのインスタンスを作り、レスポンスを返します。それ以降、サーブレットのインスタンスはアプリケーションサーバのメモリに常駐し、リクエストに対してレスポンスを返しますリクエストメソッドに応じてgetにはdoGetメソッド、postにはdoPostメソッドが呼び出されます

図3.1 サーブレットの概念

1.1. サーブレットでHello World

ここでは、ひとまずサーブレットだけを単体で実行してみることにします。JavaSEでも一番最初に学んだ「Hello World」をやってみましょう。

以下のようなサーブレットだけを作成して実行してみてください。

すると真っ白なブラウザの画面が表示されましたね。(1回目は先述の通りサーブレットのインスタンスを作ったり、場合によってはGlass Fishを立ち上げていますので時間がかかりますが、2回目以降は早くなるはずです。試してみてください。)

「Hello World」 はどこに表示されているのでしょうか?

実は、下図3.2のように「出力」のタブに「GlassFish Server x.x.x」というタブがあり、このログとして表示されているのでした。

図3.2 GlassFishのログ

このログは、JavaSEでいうところのコンソールです。GlassFish Serverのタブではプリントデバッグを行うことができます。また、各種エラーが表示されるところですから今後も頻繁に目にすることになるはずです。

ここからサーブレットの中身の解説に入りますが、ひとまず決り文句は省略して、urlPatternsとdoGetメソッドという2つの重要ポイントを解説します。

1.2. urlPatterns

10行目を再掲します。

@WebServlet(urlPatterns = {"/HelloServlet1"})

ここで注目は、urlPatterns です。urlPatterns はこのサーブレットがどのようなURLの指定で呼び出せるかを規定しています。

ブラウザのアドレスバーを見ると以下のようになっていますね。

「http://localhost:8080/03_JavaWebText/HelloServlet1」

実は、 urlPatternはJavaのクラス名とは無関係に付けることができます。しかし、当社の新人エンジニア研修では特段の理由がない限り、urlPatternは1つだけ、クラス名と同じものを用います。なお、同じプロジェクトの中に同じurlPatternを指定するとサーブレットも、JSPもHTMLさえも実行できなくなります。サーブレットをコピーして、うっかり urlPattern を書き換えるのを忘れていると起こります。(しかも、コピーした後 urlPattern を書き換えてもキャッシュに元の urlPattern が残ることがあります。その場合にはプロジェクトの「消去」が必要になりますので方法は講師にお尋ねください)一度経験しておけば安心ですので、ぜひ、試してみてください。

実験1

同じプロジェクトの中にurlPatternsが重複するサーブレットを作ると全てのファイルが実行不可能になります。

それをやってみてください。

実験2

カンの良い方は、 urlPatterns と複数形になっていることに気づかれた方もいらっしゃるかもしれません。

複数のURLのパターンを指定できるようになっています。

urlPatterns に以下のように書き足してみましょう。

@WebServlet(urlPatterns = {"/HelloServlet1","/sample"})

そうすると以下のURL指定でもこのサーブレットが実行できるはずです。

「http://localhost:8080/03_JavaWebText/sample」

コンテキストルート

urlPatternの先頭の“/”はこのサーブレットがアプリケーションの直下(プロジェクト名の直下)にできることを示しています。つまり、サーブレットのURLは以下になります。

http://ホスト名:ポート番号/プロジェクト名/urlPattern

この時、以下のポート番号までがサーバのURLです。

「http://localhost:8080/」

また、以下のよう「プロジェクト名/」までをコンテキストルートといいます。

「http://localhost:8080/03_JavaWebText/」

【context】とは英語で文脈という意味です。文脈が変われば言葉の意味が変わるように、プロジェクトが変われば同じ urlPatternでも呼び出されるサーブレットが変わります。

さらに、以下がHelloServlet1のURLです。

「http://localhost:8080/03_JavaWebText/HelloServlet1」

Servletクラス

サーブレットはHttpServletクラスを継承します。HttpServletクラスに関して当社の新人エンジニア研修では特に深入りはしません。JavaSEのときのような詳細な説明が欲しい方はJakarta EE 8 仕様 APIを御覧ください。

1.3. doGetメソッド

次にdoGetメソッドを見ていきます。

doGetメソッドは HttpServletクラスのdoGetメソッドをオーバーライドしているため@Overrideアノテーションを付けています。このメソッド内で起きる可能性のある例外を呼び出し元に投げるためにthrowsキーワードが使われていますが、決り文句ですし、JavaSEでも詳しく学んだので今回は深入りはしません。

doGetメソッドの引数は2つあり、HttpServletRequest型の変数名requestというインスタンスとHttpServletResponse型の変数名responseというインスタンスです。(本研修では単純にリクエストとレスポンスと呼ぶことにします)

それぞれ、リクエストとレスポンスでやり取りされるオブジェクトのフィールドやメソッドが含まれています。リクエストとレスポンスについては、今回の新人エンジニア研修で折に触れて紹介することになりますので、ひとまず先を急ぐことにします。

では先の文字列「Hello World」をブラウザに表示するにはどうすればよいでしょうか?

それには、JSPにデータを渡す方法とJSPのEL【Expression Language】を使って表示する方法を学ぶ必要があります。(本当はサーブレット単体でもHTMLの出力は可能ですが、MVCを学ぶという本研修の趣旨からは外れますので解説しません。)

調べてみましょう

サーブレット単体でHTMLを出力する方法を調べてみましょう。

1.4. 属性とは?

あるサーブレットからJSPへデータを渡すには属性というアプリケーション・サーバーのデータ領域を使います

そして属性にはスコープ【scope】という有効範囲があります。スコープには範囲という意味があるということを私達はJavaSEで学びました。

JavaEEにはデータの有効範囲の異なる4種類のスコープがあるのですが、今回はそのうちスコープが狭い方から2番めのリクエスト属性(Request Attributes)を使います。(本研修ではあともう一つセッション属性(Session Attributes)というものを「5.ログイン処理の実装とセッション属性」で学びます)

サーブレットではrequestのsetAttributeメソッドを使いインスタンスを属性に保存します。 set(セットする)Attribute(属性を)という意味のメソッドです。

属性には、文字列はもちろん、オブジェクトであれば何でも入ります。また、1つだけではなく複数のオブジェクトを格納することができます。複数のオブジェクトを格納できますからそれぞれに名前をつけて区別する必要があるのですね。

下図3.3のnameが名前、attributeがオブジェクトになります。

図3.3 リクエスト属性のイメージ

そして、requestのforwardメソッドを使って指定のJSPにフォワード処理をします。ちょうどサッカーのフォワードがボールを前に前に運ぶイメージでしょうか。

JSPでは、ELを使ってその値をリクエスト属性から取り出してHTMLを組み立てます。

HTMLファイルがブラウザにレスポンスされて皆さんの画面に表示されます。

次のようなサーブレットを作成して実行してみましょう。

  • 18行目のように複数のメソッドを“.”でつなぐ書き方を何といいましたか?
あなたの答え:

ポイントは、17、18行目です。

まず、17行目でリクエスト属性という保存領域にmessageという名前で"Hello World"という文字列のインスタンスを格納しています。

request.setAttribute("message", "Hello World");

("request. "と入力して暫く待つとさまざまなメソッドが表示されて面食らいますが、本研修で使用するのはほんの一部ですから安心ください)

このメソッド名をあえて日本語に訳せば、set(セットする)Attribute(属性を)という意味です。

requestのsetAttributeメソッドの引数は2つあり1つ目が名前、2つ目がオブジェクトです。

オブジェクトはリクエスト属性に複数保存できます。ただし、それぞれを区別するために違う名前が必要になります。また、この名前が遷移先のjspで変数のように利用できるわけです。

1.5. リクエスト属性の特徴

リクエスト属性は、その名の通り、1回のリクエストとレスポンスの間だけ有効な記憶領域(スコープ)です。レスポンスを返し終わると消えてしまう記憶領域ですのでメモリを圧迫しません。他のプログラミング言語ではリクエスト変数と呼ばれることもあります。

調べてみましょう

属性は全部で4つあります。

この研修ではリクエスト属性とセッション属性の2つしか使いませんが、残りの2つも余裕があれば調べてみましょう。

調べた結果のメモ:

1.6. フォワードとは

フォワードとは、処理中のサーブレットからJSP(やサーブレット)に処理を移す(画面を切り替える)ことです。JSPに画面表示の処理を任せてしまうのがフォワードです。

以下の1行でフォワードが成立します。

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

画面を切り替える方法としてあとの章でリダイレクトというものも出てきますので比較を通じて、おいおい理解いただければ大丈夫です。このメソッドの前半部分でrequestのgetRequestDispatcherメソッドを使っています

request.getRequestDispatcher("03Servlet/result.jsp")

「ブラウザにデータを戻すのであれば、 request ではなく、response のメソッドを使うのではないか?」そう思った人もいるかも知れません。しかし上図3.3をよく見ると、サーブレットからJSPにデータを渡してからレスポンスをしているのでこれで良いのですね。

このフォワードのメソッド名はget(ゲットする)Request(リクエストを)Dispatcher (送るものを)という程度の意味です。送り先のURLは引数として文字列で渡します。以下のように2通りの書き方があります。

①コンテキストルートからの相対パス

"/03Servlet/result.jsp"


②通常の相対パス

"03Servlet/result.jsp"

以下のようなルートパス指定や絶対パス指定はできません。

③ルートパス指定は不可

"/03_JavaWebText/03Servlet/result.jsp"

④絶対パス指定は不可

"http://localhost:8080/03_JavaWebText/03Servlet/result.jsp"

このパスの指定方式は初学者がつまずきやすいところです。HTMLとは違い、フォワード時は、ルートパス指定や絶対パス指定はできず、通常の相対パスまたは、コンテキストルートからの相対パスで指定しないといけないことを記憶してください。HTMLで学んだ3つのパス指定の中で相対パスしか使えないということに気をつけましょう。


今回この処理はメソッドチェーンを使い1行で書いています。2行で書くこともできますが、シンプルですからこの書き方に慣れてください。なお、以下の部分は決り文句と考えていただいて結構です。

.forward(request, response)

初めてServletを学んだときに面食らうのがforward後のURLです。

となっています。JSPのURLは以下の通りですが、表示はサーブレットのURLのままですね。

したがってもしもエラーが起きた場合に問題の切り分けが困難になることがあります。つまり、エラーの発生箇所がサーブレットなのか、JSPなのかがすぐには判断しづらいことがあります。

1.7. ELを使ったリクエスト属性のオブジェクトの取り出し方

JSPには、HTMLの定型文をすべて消して、ELだけを記述することにします。論点を明確にするためです。

下記のようにJSPのELは$で始めて波括弧の中にリクエスト属性に付けた名前を書けば表示されます。この例では”message"です。

${message}

ただし、この書き方ですとこの後セッション属性というものが出てきたときに区別できません。そこでリクエスト属性であることを明確化するために以下のように書くことにします。

${requestScope.message}

また、このとき、もちろんJSPだけを単体で実行しても何も表示されません。サーブレットからJSPにフォワードしないとHello Worldが表示されないことを確認してください。

なお、変数に$マークを付けるのはIT業界の古くからの習慣です。古くはBasic、そしてLinuxのシェルスクリプトやPHPでも$は変数の目印として使われます。豆知識として覚えておいて損はありません。

2. JSPで文字化けを起こさずに日本語を扱う

先のプログラムを少しだけ変更して日本語で挨拶させてみます。

以下のサンプルコードを実行してみましょう。(実行の前にフィルタを外す必要のある場合があります)

するとブラウザには「?????」のように文字化けして表示されたのではないでしょうか?この文字化けを解消するにはJSPに文字コードとしてUTF-8を指定します。

以下のサンプルコードをご覧ください。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>result2.jsp</title>
    </head>
    <body>
        ${requestScope.message}
    </body>
</html>

実験3

上記サンプルコードresult2.jspのmessageにIDEの名前変更機能は有効でしょうか?

実験結果のメモ:

3. JSPの正体はサーブレットである

ここまで、サーブレットとJSPはあたかも別物として説明してきました。ControllerとViewを明確に分けるという意味ではその説明にも一理あります。しかし、エンジニアの皆さんは一歩進んでJSPも実はサーブレットなのだということを知っておきましょう。JSPもサーブレットに変換されてからサーバーサイドで動くのです。

先の、ELが一行だけ書かれたresult.jspをサーブレットに変換すると以下のようになります。(やり方は講師にお尋ねください)

なお、大変複雑なJavaコードですが、理解する必要はありません。

  • 答えを出力するためのELが上のソースコードの何行目にあったか分かりますか?
あなたの答え:

JSPをサーブレットに変換したソースコードを理解する必要はありません。しかし、JSPで上手く表示されないときにサーブレットにしてコードを解析すると問題が解決することもあります。

実験4

以下のソースコードにはJSPのコメントとHTMLのコメントが書かれています。

このファイルを実行して「ページのソースを表示」を選択してみてください。

何か分かったことはありますか?

また、なぜそのような違いがあるのでしょうか?

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>comment.jsp</title>
    </head>
    <body>
        <%-- これはJSPのコメントです --%>
        <!--これはHTMLのコメントです-->
    </body>
</html>
実験結果のメモ:

例題1

以下のサーブレットを実行したときにブラウザには何が出力されますか?

なお、各ファイルはそれぞれ正しい構成でプロジェクト内にあるものとします。

${requestScope.ans}
あなたの答え:

例題2

上記のELを以下のように書き換えるとします。

何が出力されるか予想しなさい。

${ans + 1}

あなたの答え:

例題3

上記例題1のサーブレットのdoGetメソッドをdoPostメソッドで書き換えたとします。

実行したときにブラウザには何が出力されますか?

あなたの答え:

doPostメソッドしか持たないサーブレットは単体で実行できますか?

あなたの答え:

今回はサーブレットからJSPにデータを渡して表示する方法について学びました。

次回は、フォームから送ったデータをサーブレットで受け取る方法を学んでいきます。

そうすれば、ブラウザからのデータをサーブレットで受けて、JSPで表示する一連のMVCの処理が書けるようになります。

インターネットでサンプルコードを探す際の注意点

学習が進んでシステム開発演習をするようになるといろいろなサンプルコードをインターネット検索することもあるでしょう。その際に、以下の9~11行目のような<% %>で囲われた記述を目にすることもあるでしょう。

これはスクリプトレット【 scriptlet 】といいます。script=プログラム、let=短い。ということで文字通りJSPの中に短いJavaのプログラムが書ける仕様です。

先に学んだ日本語を表示させるプログラムをJSP1つに書くと以下のようになります。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>scriptlet</title>
    </head>
    <body>
        <%
            request.setAttribute("message", "こんにちは");
        %>
        ${message}
    </body>
</html>

ずいぶんコードが短くなり、見通しがよくなりました。

しかし、当社の新人エンジニア研修では後の1章でのみ便宜的に使うのを除いて、スクリプトレットを使いません。

なぜなら、JSPにロジックを書くことはMVCモデルの考え方に反し保守性を低下させるから、というのは以前お話した通りです。

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

□ Java Servletとは、Javaで書かれたプログラムであり、サーバ上で動く

□ サーブレットのインスタンスはアプリケーションサーバのメモリに常駐し、リクエストに対してレスポンスを返す

□ リクエストメソッドに応じてgetにはdoGetメソッド、postにはdoPostメソッドが呼び出される

□ GlassFish Serverのタブではプリントデバッグを行うことができ、また、各種エラーが表示される

□ urlPatternsはこのサーブレットがどのようなURLの指定で呼び出せるかを規定しているが本研修では基本的にクラス名と同じにしている

□ 同じプロジェクトの中に全く同じurlPatternを指定したサーブレットが複数あると、JSPもHTMLさえも実行できなくなる

□ サーブレットのURLはhttp://ホスト名:ポート番号/プロジェクト名/urlPatternである

□ doGetメソッドの引数は2つあり、HttpServletRequest型のインスタンスとHttpServletResponse型のインスタンスである

□ サーブレットからJSPへデータを渡すにはリクエスト属性という使い捨てのデータ領域を使うのが一般的である

□ requestのsetAttributeメソッドの引数は2つあり、1つ目が名前、2つ目がオブジェクトである

□ requestのgetRequestDispatcherメソッドを使いJSPにフォワード処理する

□ フォワード時は、ルートパス指定や絶対パス指定はできず、通常の相対パスまたは、コンテキストルートからの相対パスで指定しないといけない

□ JSPのELは$で始めて波括弧の中にリクエスト属性に付けた名前を書く

□ JSPもサーブレットに変換されて実行される

JavaWebアプリケーション目次に戻る