前回はMVCモデルについて概略を学びました。
今回はコントローラーの役目を果たす(ことが多い)Servletについて学んでいきましょう。
1. Servletとは?
Java Servletとは、Javaで書かれたプログラムであり、サーバ上で動きます。以前はJava Appletというクライアント(ブラウザ)側で動くJavaプログラムがありJava Servletと対になっていたためこの名前があります。(現在では Java Appletはほとんど使われていません)
Servletの概念は下図3.1の通りです。
ブラウザからアプリケーションサーバにリクエストが送られると初回のみServletコンテナがJavaのクラスファイルを実行してServletのインスタンスを作り、レスポンスを返します。それ以降、Servletのインスタンスはアプリケーションサーバのメモリに常駐し、リクエストに対してレスポンスを返します。リクエストメソッドに応じてgetにはdoGetメソッド、postにはdoPostメソッドが呼び出されます。
1.1. ServletでHello World
ここでは、ひとまずServletだけを単体で実行してみることにします。JavaSEでも一番最初に学んだ「Hello World」をやってみましょう。
以下のようなHelloServlet1.javaだけを作成して実行してみてください。(14~15行目の記述は通常は1行ですが、今回のみ印刷時にA4サイズに収まるように折り返していますのでご了承ください)
package controller;
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(urlPatterns = { "/Hello1" })
public class Hello1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("Hello World");
}
}
すると真っ白なブラウザの画面が表示されましたね。(1回目は先述の通りServletのインスタンスを作ったり、場合によってはTomcatを立ち上げていますので時間がかかりますが、2回目以降は早くなるはずです。試してみてください。)
「Hello World」 はどこに表示されているのでしょうか?
実は、下図3.2のように「コンソール」というタブがあり、このログとして表示されているのでした。
このログは、JavaSEでいうところのコンソールです。コンソールではプリントデバッグを行うことができます。また、各種エラーが表示されるところですから今後も頻繁に目にすることになるはずです。
ここからServletの中身の解説に入りますが、ひとまず決り文句は省略して、urlPatternsとdoGetメソッドという2つの重要ポイントを解説します。
1.2. urlPatterns
10行目を再掲します。
@WebServlet(urlPatterns = {"/Hello1"})
ここで注目は、urlPatterns です。urlPatterns はこのServletがどのようなURLの指定で呼び出せるかを規定しています。
ブラウザのアドレスバーを見ると以下のようになっていますね。
「localhost:8080/03_JavaWeb03Servlet/Hello1」
実は、 urlPatternはJavaのクラス名とは無関係に付けることができます。しかし、当社の新人エンジニア研修では特段の理由がない限り、urlPatternは1つだけ、クラス名からServletを削除したものを用います。
なお、同じプロジェクトの複数のServletの中に同じurlPatternを指定するとServletも、JSPもHTMLさえも実行できなくなります。Servletのファイルをコピーして、うっかり urlPattern を書き換えるのを忘れていると起こります。(しかも、コピーした後 urlPattern を書き換えてもキャッシュに元の urlPattern が残ることがあります。その場合にはプロジェクトの「リフレッシュ」が必要になりますので方法は講師にお尋ねください)一度経験しておけば安心ですので、ぜひ、試してみてください。
コンテキストルート
urlPatternの先頭の“/”はこのServletがアプリケーションの直下(プロジェクト名の直下)にできることを示しています。つまり、ServletのURLは以下になります。
http://ドメイン名:ポート番号/プロジェクト名/urlPattern
この時、以下のポート番号までがサーバのドメインです。
「http://localhost:8080/」
また、以下の「プロジェクト名/」までをコンテキストルートといいます。
「http://localhost:8080/03_JavaWeb03Servlet/」
【context】とは英語で文脈という意味です。文脈が変われば言葉の意味が変わるように、プロジェクトが変われば同じ urlPatternでも呼び出されるServletが変わります。
さらに、以下がHelloServlet1のURLです。
「http://localhost:8080/03_JavaWeb03Servlet/Hello1」
なお、前章で見たように${pageContext.request.contextPath}というEL式を使い「/プロジェクト名」を取得することができます。つまりこの章の場合は「/03_JavaWeb03Servlet」です。プロジェクト名は変更になることもありますので直接記述するよりも賢い方法です。
Servletクラス
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】を使って表示する方法を学ぶ必要があります。(本当はServlet単体でもHTMLの出力は可能ですが、MVCを学ぶという本研修の趣旨からは外れますので解説しません。)
1.4. 属性とは?
あるServletからJSPへデータを渡すには属性というアプリケーション・サーバーのデータ領域を使います。
そして属性にはスコープ【scope】という有効範囲があります。英語の【scope】には範囲という意味があるということを私達はJavaSEで学びました。
JavaEEにはデータの有効範囲の異なる4種類のスコープがあるのですが、今回はそのうちスコープが狭い方から2番めのリクエスト属性(Request Attributes)を使います。(本研修ではあともう一つセッション属性(Session Attributes)というものを「5.ログイン処理の実装とセッション属性」で学びます)
ServletではrequestのsetAttributeメソッドを使いインスタンスを属性に保存します。 【Attribute】=属性を、【set】=セットする、という意味のメソッドです。
属性には、文字列はもちろん、オブジェクトであれば何でも入ります。また、1つだけではなく複数のオブジェクトを格納することができます。複数のオブジェクトを格納できますからそれぞれに名前をつけて区別する必要があるのですね。
下図3.3のnameが名前、attributeがオブジェクトになります。
そして、requestのforwardメソッドを使って指定のJSPにフォワード処理をします。ちょうどサッカーのフォワードがボールを前に前に運ぶイメージでしょうか。
JSPでは、ELを使ってその値をリクエスト属性から取り出してHTMLを組み立てます。
HTMLファイルがブラウザにレスポンスされて皆さんの画面に表示されます。
次のようなServletを作成して実行してみましょう。(19~22行目の記述は通常は1行ですが、今回のみ印刷時にA4サイズに収まるように折り返していますのでご了承ください)
package controller;
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(urlPatterns = { "/Hello2" })
public class Hello2Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("message", "Hello World");
request.getRequestDispatcher("/view/result.jsp").forward(request, response);
}
}
- 「request.getRequestDispatcher("/view/result.jsp").forward(request, response)」のように複数のメソッドを“.”でつなぐ書き方を何といいましたか?
あなたの答え: |
ポイントは、18行目以降です。
まず、18行目でリクエスト属性という保存領域にmessageという名前で"Hello World"という文字列のインスタンスを格納しています。
request.setAttribute("message", "Hello World");
このメソッド名は「属性をセットする」というそのままの意味でしたね。
requestのsetAttributeメソッドの引数は2つあり、1つ目が名前、2つ目がオブジェクトです。
オブジェクトはリクエスト属性に複数保存できます。ただし、それぞれを区別するために違う名前が必要になります。また、この名前が遷移先のjspで変数のように利用できるわけです。
1.5. リクエスト属性の特徴
リクエスト属性は、その名の通り、1回のリクエストとレスポンスの間だけ有効な記憶領域(スコープ)です。レスポンスを返し終わると消えてしまう記憶領域ですのでメモリを圧迫しません。他のプログラミング言語ではリクエスト変数と呼ばれることもあります。
1.6. フォワードとは
フォワードとは、処理中のServletからJSP(やServlet)に処理を移す(画面を切り替える)ことです。JSPに画面表示の処理を任せてしまうのがフォワードです。
以下の1行でフォワードが成立します。
request.getRequestDispatcher("/view/result.jsp").forward(request, response);
画面を切り替える方法としてあとの章でリダイレクトというものも出てきますので比較を通じて、おいおい理解いただければ大丈夫です。このメソッドの前半部分でrequestのgetRequestDispatcherメソッドを使っています。
request.getRequestDispatcher("/view/result.jsp")
「ブラウザにデータを戻すのであれば、 request ではなく、response のメソッドを使うのではないか?」そう思った人もいるかも知れません。しかし上図3.3をよく見ると、ServletからJSPにデータを渡してからレスポンスをしているのでこれで良いのですね。
このフォワードのメソッド名は【Request】=「リクエストを」【Dispatcher】= 「送るものを」【get】=「ゲットする」という程度の意味です。送り先のURLは引数として文字列で渡します。以下のように2通りの書き方があります。この研修ではフォワード先のパス指定は①コンテキストルートからの相対パスで統一したいと思います。
①コンテキストルートからの相対パス(本研修ではこの書き方に統一します)
"/view/result.jsp"
②通常の相対パス(本研修ではこの書きは使用しません)
"view/result.jsp"
以下のようなルートパス指定や絶対パス指定はできません。
③ルートパス指定は不可
"/03_JavaWeb03Servlet/view/result.jsp"
④絶対パス指定は不可
"http://localhost:8080/03_JavaWeb03Servlet/view/result.jsp"
このパスの指定方式は初学者がつまずきやすいところです。HTMLとは違い、フォワード時は、ルートパス指定や絶対パス指定はできず、コンテキストルートからの相対パスで指定しないといけないことを記憶してください。(または通常の相対パス)見分け方としては③④にはプロジェクト名がはいっているので、フォワード先にプロジェクト名を書いてはダメだと記憶してください。
今回この処理はメソッドチェーンを使い1行で書いています。2行で書くこともできますが、シンプルですからこの書き方に慣れてください。なお、以下の部分は決り文句と考えていただいて結構です。
.forward(request, response)
初めてServletを学んだときに面食らうのがforward後のURLです。
「http://localhost:8080/03_JavaWeb03Servlet/Hello2」
となっています。JSPのURLは以下の通りですが、表示はServletのURLのままですね。
「http://localhost:8080/03_JavaWeb03Servlet/view/result.jsp」
したがって、もしもエラーが起きた場合に問題の切り分けが困難になることがあります。つまり、エラーの発生箇所がServletなのか、JSPなのかがすぐには判断しづらいことがあります。
- フォワードした後の行に何か処理を書いた場合、その処理は実行されますか?
あなたの答え: |
1.7. ELを使ったリクエスト属性のオブジェクトの取り出し方
result.jspには、HTMLの定型文をすべて消して、ELだけを記述することにしました。論点を明確にするためです。
下記のようにJSPのELは$で始めて波括弧の中にリクエスト属性に付けた名前を書けば表示されます。この例では”message"です。
${message}
ただし、この書き方ですとこの後セッション属性というものが出てきたときに区別できません。そこでリクエスト属性であることを明確化するために以下のように書くことにします。
${requestScope.message}
また、このとき、もちろんJSPだけを単体で実行しても何も表示されません。ServletからJSPにフォワードしないとHello Worldが表示されないことを確認してください。
なお、変数に$マークを付けるのはIT業界の古くからの習慣です。古くはBasic、そしてLinuxのシェルスクリプトやPHPでも$は変数の目印として使われます。さらにJavaScriptのテンプレートリテラルでは全く同じ表記をします。豆知識として覚えておいて損はありません。
今回はServletからJSPにデータを渡して表示する方法について学びました。
次回は、フォームから送ったデータをServletで受け取る方法を学んでいきます。
そうすれば、ブラウザからのデータをServletで受けて、JSPで表示する一連のMVCの処理が書けるようになります。