Servlet と JSP (Java Server Pages)
Java Servlet を利用するには、Webサーバーに Servlet エンジンを追加する必要があります(Apache + Tomcat など)。Servlet は、CGI と同じような Web サーバーの拡張技術です。CGI が、標準入出力を用いたインターフェースでプロセス単位での実行なのに対し、Servlet にはAPIが用意されておりスレッド単位で実行されます。
Servlet の動きを、ブラウザーからの GET コマンドに対して HTML を送信する Servlet プログラムをもとに説明します。
Web サーバー |
Servlet エンジン |
Servlet プログラム(HelloWorld.java) |
GETコマンド |
ブラウザーから送られてきたデータを HttpServletRequestオブジェクトに格納する HttpServletRequestオブジェクトを引数に doGet()メソッドを呼び出す。 |
import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld extends HttpServlet { public void init(ServletConfig conf) throws ServletException { super.init(conf); } public void destroy() { super.destroy(); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException |
HTML |
HttpServletResponseオブジェクトを介してブラウザーに送信します。 | { PrintWriter out; res.setContentType("text/html; charset=EUC-JP"); out = res.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Hello World</title>"); out.println("</head>"); out.println("<body bgcolor=\"white\">"); out.println("<h1>Hello World</h1>"); out.println("</body>"); out.println("</html>"); out.flush(); out.close(); } } |
ブラウザーから HTTP の GET コマンドを受け取ると、Servlet エンジンは初期化(init()を実行)を行い doGet() メソッドを呼び出します。doGetメソッドを呼び出す際に、引数として HttpServletRequest オブジェクトと HttpServletResponseオブジェクトを渡します。HttpServletRequest オブジェクトは、ブラウザーから送られた情報を格納しています。HttpServletResponseオブジェクトは、ブラウザーへの送信機能を持っています。ブラウザーに、レスポンスを返して後処理(destroy()を実行)を行い終了します。ここで、HTTP の POST コマンドを受け取った場合 doPost() メソッドが呼び出されます。 |
Servlet プログラムを細かく見てみると、プログラムは、HttpServlet クラスを継承して作成します。
public class HelloWorld extends HttpServlet
ブラウザーが送信したコマンドに対応したメソッドを記述します。GET コマンドの場合 doGet() メソッド、POST コマンドの場合 doPost() メソッド。また、ブラウザーからの Cookie などの情報が HttpServletRequest オブジェクトに格納され、ブラウザに返す情報を HttpServletResponse オブジェクトに格納するので、それらのオブジェクトを引数として doGet() メソッドや doPost() メソッドを記述します。
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException
上記のサンプルは非常に単純なため記述していないが、前処理として初期化や終了時に後処理が必要な場合には init(), destory() メソッドにそれぞれの処理を記述することで Servlet エンジンが実行してくれます。
doGet() メソッドには、HttpServletRequest オブジェクトに格納された情報を解析し、結果を HttpServletResponse オブジェクトに格納してブラウザーに送信するような処理を記述します。サンプルでは、単純にブラウザーへ HTML を送信するだけで、HttpServletResponse オブジェクトから出力用のストリーム PrintWriterを取り出し、それを介して HTML を送信しています。
PrintWriter out = res.getWriter(); out.println("<html>"); out.println("<head>"); ...
サンプルでは、直接 HTML を出力するルーチンがプログラムにハードコーディングされているが、実際に Servlet を作る場合、変更の無い HTML データを別途ファイルに記述しておき、そのファイルを読み込むような構造にすると良い。このことで、画面レイアウトに変更があっても Servlet プログラムを再コンパイルする必要が無くなり、プログラム自体も見やすくなります。この時、HTML データを記述しておいたファイルの I/O が負荷になる恐れがあるので、doGet(), doPost() メソッド内に HTML データを記述したファイルを読み込むルーチンを記述するのではなく、init() メソッドに記述しておくことで負荷が軽減されます。
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld2 extends HttpServlet { protected LoadHTML loadhtml; public void init(ServletConfig conf) throws ServletException { loadhtml = new LoadHTML("/usr/local/servlet/HelloWorld.html"); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { PrintWiter out = res.getWriter(); loadhtml.printHTML(out); out.close(); } }
※LoadHTML クラスや printHTML() メソッドは、ファイルから HTML データを読み込む処理を行ないます。(実際のコードは省略します)
Microsoft の ASP (Active Server Page) のように、HTMLの中にプログラムを埋め込んで動きのあるページを作成するために JSP (Java Server Pages) というものがあります。これは、ブラウザが HTML ファイルを要求するたびに、埋め込んであるプログラムが実行されその結果を元にした HTML がブラウザーに送られます。結果の変化する部分をプログラムとして HTML の中に記述できることで、非常に簡単に動きのあるページを作成することができます。ASP との違いは、VBScript ではなく Java で記述するということと、VBScriptのように毎回インタプリタが逐次翻訳(正確にはコンパイル後、別個のファイルではなくメモリーに入れられる)するのではなくプログラム部分がコンパイルされるので2回目以降から実行速度が高速になります。ソースに変更があった場合、自動的に再コンパイルされます。
ブラウザーから JSP ファイルが要求されると、JSP エンジンがプログラム部分を Servlet に変換しコンパイルされます。コンパイルされた Servlet プログラムを Servlet エンジンが実行し、結果を JSP ファイルのプログラムの部分と入れ替え HTML としてブラウザーに送信します。
記述方法は、HTML ファイル内にプログラムとして実行したい部分を <% と %> で囲んで Java のコードを記述します。ファイル名の拡張子は、HTML と区別するために .jsp とします。
例えば、アクセス数をカウントする JSP ファイルを作成すると以下のようになります。
<HTML> <HEAD> <TITLE>Access Counter</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <DIV ALIGN="CENTER"><FONT SIZE="+1" COLOR="#0000FF">Access Counter</FONT></DIV> <BR> <%! protected int total = 0; %> <% // Request オブジェクト内の Cookie の値から Session ID を取り出し参照 session = request.getSession(true); // Session オブジェクト内の値を取り出す Integer num = (Integer)session.getValue("counter"); if (num == null) { num = new Integer(1); } else { num = new Integer(num.intValue()+1); } // Session オブジェクトに値を設定 session.putValue("counter", num); %> <DIV ALIGN="CENTER"> あなたのアクセス回数 <%= num %> 回<BR> 全体のアクセス回数 <%= ++total %> 回<BR> </DIV> </BODY> </HTML>
JSPは、Servlet に変換されるので Servlet エンジンが提供するオブジェクトを利用できます。JSP を詳しく見てみると、total 変数を利用して全体のアクセス数をカウントしています。ブラウザーごとのアクセス数は、Session オブジェクトを利用します。Session オブジェクトは、ブラウザーごとに生成するサーバー側のオブジェクトです。Cookie に埋め込んだ Session ID によって Session オブジェクトを特定します。Session オブジェクトの生成は、
session = request.getSession(true);
となり、request は、Servlet で説明した HttpServletRequest 型のオブジェクトです。getSession メソッドは、Cookie から Session ID を取り出し、該当する Session オブジェクトを返します。Session ID がセットされていなかった場合、初めてのアクセスと解釈して Session オブジェクトを生成して新しい Session ID を割り当てます。あとは、getValue(), setValue() メソッドを利用してアクセス数の値を取り出したり、インクリメントした値をセットしたりします。
JSP は Servlet の別の表現方法で、Servlet を HTML ページとして見ることができるようにしたものです。JSP により、簡単に動きのあるページを作れるようになります。JSP は、小規模で単純なページには向いています。しかし、大規模なページの場合、ページデザインと Java をコーディングする人が異なったり、HTML, JavaScript, Java コードの混在でデバッグが非常に困難なものになってしまいます。また、引き継いだ開発者が全体のフローを理解するにもすべてのページを見直さなければなりません。そのため、複雑な Web アプリケーションの場合 Servlet だけを用いてロジックの部分と描画部分(HTML) を分割したコーディングのほうがわかりやすいことがあります。
そこで、JSP 1.1 では、タグと呼ばれる機能を導入しています。JSPタグは、JSPファイルから Javaコードをくくるための手法です(JSPファイル中の Javaコードをタグというマクロにしたような感じです)。JSPタグにより、HTML 内の Java コードを少なくしてコードを見やすくすることで開発の分業が行いやすくなります。また、これらの JSPタグやそのタグの実装を再利用可能な形でまとめたフレームワークとして Struts というものがオープンソースとして提供されています。JSP タグや Struts を利用することで、大規模な Web アプリケーション開発が非常に楽になります。