独り言

プログラミングの講師をしています。新人研修で扱う技術の解説と個人の技術メモ、技術書の紹介など

【Java】JSP/サーブレット入門

JSPサーブレットは、JavaEEに含まれる技術です。 APサーバーであるTomcatが提供している機能になります。
JSPサーブレットは主にJavaを使用してWebアプリケーションを作成する際に使用される技術です。

前提として、Javaの基本的な知識とHTMLの基本的な知識がある前提で進めていきます。
JavaとHTMLについては以下の記事を参照ください。

case10.hateblo.jp

case10.hateblo.jp

case10.hateblo.jp


目次


環境構築

JSP/サーブレットを利用するには、Javaの動作環境(JDK)とAPサーバー(Tomcat)の環境が必要になります。
JSP/サーブレットの動作環境はVS Codeでも作成することが可能ですが、IDE(eclipse)を使用したほうが楽なので、IDEを使用することを推奨します。

IDEによる環境構築

IDEとは、統合開発環境と呼ばれる、開発に必要なツール群がひとまとまりになっているソフトウェアです。
ここではJavaIDEとしてよく利用されているEclipseでの手順を紹介します。

eclipseの環境がない場合は
https://mergedoc.osdn.jp/
からダウンロードしてください。
様々なバージョンがありますが、基本的に最新バージョンで問題ありません。
Javaの「Full Edition」を使用しているOSに合わせてダウンロードしてください。
Full Editionをダウンロードした場合、JDKTomcatが既に含まれているため、特別な設定は不要です。
eclipse内でJavaTomcatのバージョンの設定をすれば動作させることが可能です。

起動したら新規プロジェクトで「動的Webプロジェクト」を選んで作成します。
動的プロジェクトは、「ファイル」⇒「新規」⇒「その他」⇒「Web」⇒「動的Webプロジェクト」から選択できます。
プロジェクト名は任意です。
プロジェクト作成時のダイアログにある「ターゲット・ランタイム」でTomcatのバージョンを指定できます。
最新のもの(これを書いてる時点ではTomcat9)を選んで問題ありません。
プロジェクトが作成できたら、「WebCondent」フォルダの配下にJSPファイルを作成します。
JSPファイルは、WebContentフォルダを選択し、「右クリック」⇒「新規」⇒「その他」⇒「Web」⇒「JSPファイル」から作成できます。

VS Codeによる環境構築

VS Codeによる環境構築方法も紹介しておきます。
まずはVS CodeTomcat拡張機能をインストールします。
拡張機能で「Tomcat for Java」をインストールします。
Tomcatを使用するにはTomcatのファイルが必要になります。
公式サイト
http://tomcat.apache.org/
からダウンロードしておきましょう。
Javaのバージョンによって対応しているバージョンが異なりますが、
http://tomcat.apache.org/whichversion.html
で対応バージョンを確認してダウンロードしておきましょう。
Javaの環境構築に関してはJava入門(導入~メソッド)の記事を参照ください。

zipファイルをダウンロードして、適当な場所(Cドライブ直下など)に展開しておいてください。

コマンドパレット(Ctrl + Shift + p)を開き、
Tomcat: Add Tomcat Server
を選択し、あらかじめ展開していたTomcatサーバーを指定します。

追加したら左の「TOMCAT SERVER」ペインで確認できます。
コマンドパレットから
Tomcat: Start Tomcat Server
でサーバーが起動します。
ブラウザから
localhost:8080
にアクセスしてTomcatの画面が表示されたらAPサーバーの確認はOKです。

Tomcatの確認ができたらJavaのプロジェクトを作成していきます。
コマンドパレット(Ctrl + Shift + p)から「java:Create Java Project」を選択し、Javaプロジェクトを作成します。
プロジェクトのフォルダ直下にJSPファイルを作成します。

ソースの作成が完了したらコマンドパレットから
Tomcat: Generate War Package From Current Folder
を選択します。
プロジェクトの直下にwarファイルが作成されます。
warファイルが作成されたら、コマンドパレットから
Tomcat: Run on Tomcat Server
を選択し、追加したTomcatサーバーにwarファイルをデプロイします。

うまくいけばブラウザから
loaclhost:8080/プロジェクト名/JSPファイル名 でアクセス可能になります。

※TODO
Mavenでの作成でもっと簡単にできる方法がありそうだけど試してもできなかったので
そのうちできたら追記。

warファイル

VS CodeJSPを実行する際に「warファイル」と呼ばれるファイルが出てきました。
このファイルについても説明しておきます。
JavaでのWebアプリケーションの開発が完了したら、作ったプログラムを実際にWebアプリケーションを動かすサーバー上に置いて動かせるようにしなければいけません。
この作業のことをデプロイとよびます。
Javaで書いたソースコードは実際にプログラムを動かすときには不要です。(コンパイルされたclassファイルがあれば動く)
そのため、開発するときに必要なファイルと、実際に動作させるときに必要なファイルは必ずしも一致しません。
そこで、サーバー上に配置するだけで動くように、必要なファイルを決められたフォルダ構成でまとめられたファイルがwarファイルです。
warファイルは実際にはただの圧縮ファイルです。
そのため、出力されたwarファイルの拡張子をzipなどに変更し、展開することで中身を確認することができます。

TODO
* VS CodeでのJSPデバッグ方法


JSPの基本

JavaServerPagesの略です。
HTMLにJavaのコードを埋め込むことで、動的なWebページを作成するための技術です。
Javaを用いてWebアプリケーションを作成する際に利用されます。

ここではHTMLについての知識はある程度知っている前提で解説していきます。
まずは以下のJSPファイルを作成します。

sample.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<%-- コメントです --%>
<% 
    out.println("<p>おはよう</p>"); 
    out.println("<p>こんにちは</p>"); 
%>
</body>
</html>

ファイルが作成できたらTomcatを起動し、ブラウザから
htttp://localhost:8080/{プロジェクト名}/sample.jsp
にアクセスします。

うまくいけばブラウザに「おはよう」と「こんにちは」が表示されます。
HTMLについての解説はここでは省略します。

拡張子

JSPファイルの拡張子は「.jsp」となります。

ページディレクティブ

先頭の部分の「<%@ ... %> 」をページディレクティブといいます。 JSPに関する設定情報などを書きます。

スクリプトレット

body部の「<% ... %>」の部分をスクリプトレットといいます。
スクリプトレットの中には任意のJavaの処理が書くことができます。
ただし、クラスの定義やメソッドの定義をすることはできません。
通常のJavaファイルでいうところの、メソッドの中身に該当する、上から順に流れる処理の流れを定義します。
(厳密にはJSP内にメソッドを定義することはできますが、通常のスクリプトレットではできません。)

実際はJSPJavaのプログラムです。
内部ではサーブレットと呼ばれるJavaのクラスに変換されます。
なので、クラスとメソッドの定義があり、その中に処理が書かれています。
JavaのプログラムでHTMLの出力が行われ、それがブラウザ上に表示される仕組みです。

スクリプトレットは1つのファイル内に複数書くことも可能です。
以下の様にスクリプトレットを分けて書くこともできます。

<% out.println("<p>おはよう</p>"); %>
<% out.println("<p>こんにちは</p>"); %>

ブロックを跨いで書くことも可能です。

<% int n = 10; %>
<% if(n > 5) { %>
  <p>こんにちは</p>
<% } %>

ブラウザへの出力

通常のJavaのアプリケーションでは、「System.out.println」によってコンソールに値を出力することができました。
しかしJSPはHTMLに変換されてブラウザに表示されるため、出力する際にはブラウザに出力されほしいところです。
JSP(とサーブレット)では、ブラウザへの出力ができるクラスが用意されており、それをJSP上ではoutという変数で使用することが可能となっています。

「out.println」や「out.print」といったメソッドを使用すると、ブラウザに値を出力することができます。
出力した値は、HTML上の文字として出力されます。
printメソッドとprintlnメソッドの違いは、出力後に改行が入るかどうかです。
printメソッドでは出力後に改行が入らず、printlnメソッドでは出力後に改行が入ります。
(System.out.printなどと同じです。)
ただし、HTMLの場合、改行を含む文字でもブラウザで表示すると改行されません。
ブラウザ上で改行されるようにするには
タグや

タグを使用する必要があるので、ブラウザ上で改行させたい場合にはタグも一緒に出力されるようにしましょう。

暗黙オブジェクト

out.printlnは、System.out.printlnとは異なるものです。
Javaを学習するとき、コンソールへの出力としてSystem.out.printlnをよく利用しますが、JSPでのout.printlnは、ブラウザ上に出力される処理なので、似ているようで全くの別物です。
JSPの処理でもコンソールへの出力を行いたい場合は、System.out.printlnを利用することができます。

ところでこの「out」は何者でしょうか。
通常Javaでは変数を宣言して、インスタンスを作成しなければメソッドを使用することはできません。
しかしJSPのソース上ではoutというインスタンスを定義している箇所はありません。

実はJSPには、暗黙オブジェクトと呼ばれるものが存在します。
これは、内部で予め定義されていて、明示的に宣言しなくてもJSP内であれば使用することができるオブジェクトです。
JSPは一度Javaのファイルの変換されます。
暗黙オブジェクトは、その変換されたJavaファイルのクラスの中で定義されています。
outはその暗黙オブジェクトの中の一つです。
暗黙オブジェクトは他にもいくつかの種類がありますが、それらについては後で説明します。

コメント

JSPではHTMLとは別でコメントの機能が用意されています。 <%-- --%> この部分に書いた内容がコメントになります。

処理に影響を及ぼさない、という点では、HTMLのコメントと同じですが、 一つだけHTMLのコメントと異なる部分があります。 それは、JSPのコメントはHTMLのコメントに残らないという点です。 HTMLのコメントは、ブラウザからソースコードを見た時に、コメントの内容を見ることができます。 しかし、JSPのコメントは、ブラウザからHTMLのソースコードを見ても残っていません。 これは、JSPは内部的にはJavaのプログラムであり、プログラムが実行された結果としてHTMLファイルが作成される、という仕組みのため、HTMLにはコメントが残らないようになっています。 スクリプトレットの中では、Javaで使用するコメント(// や / /)も使用することができます。

JSP

先ほどのサンプルでは「out.println」によりブラウザへの出力を行いましたが、実はもっとシンプルに値を出力することもできます。
<%= 値 %> と書くと、out.printlnと同様に値をブラウザに出力することができます。

<% 
    out.println("<p>こんにちは</p>"); 
%>
<%= "<p>こんにちは</p>" %>

出力のみを行う処理の場合、JSP式を使用したほうが簡潔に処理が書けるので、活用するとよいでしょう。


JSPをさらに詳しく

pageディレクティブ

Pageディレクティブとは、JSPのページの先頭に書かれている以下のような設定情報のことでした。

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>

language

言語の設定です。
基本的にJavaになります。

contentType

デフォルトではHTMLとなっていますが、画像やテキストファイルのページとしたければここで指定することができます。
charsetは文字コードです。

pageEncoding

JSPページの文字コードです。

これ以外にもpageディレクティブで設定できる項目があります。

import

Javaにおけるimportと同じものです。
JSP内でJavaのライブラリや自作のクラスを使用したい場合に使用します。
複数のimport文を書く場合にはカンマ区切りで書くことも可能です。
<%@ page import="java.util.List , java.util.ArrayList" %>

errorPage

エラーが発生した場合に表示するページを設定します。

他にも色々なディレクティブがありますが、興味があれば調べてみましょう。


暗黙オブジェクト

暗黙オブジェクトは、JSPの中で宣言せずとも使用することができるオブジェクトのことです。
先の例でoutという変数を使ってブラウザへの出力処理を行いましたが、このoutが暗黙オブジェクトの一つです。

JSPで使用することができる暗黙オブジェクトは以下の通りです。

  • pageContext
  • request
  • session
  • application
  • response
  • out
  • page
  • config
  • exeption

pageContext

javax.servlet.jsp.PageContextのオブジェクトです。
JSPのオブジェクトを管理しているクラスです。
ページスコープの操作を行う際に使用します。

request

javax.servlet.http.HttpServletRequestのオブジェクトです。
クライアントからのリクエスト情報を取得したり、リクエストスコープに関する処理を行う際などに使用します。

session

javax.servlet.http.HttpSessionクラスのオブジェクトです。
セッション情報の管理に使用します。

response

javax.servlet.http.HttpServletResponseのオブジェクトです。
クライアントへのレスポンス情報の設定で使用します。

out

javax.servlet.jsp.JspWriterのオブジェクトです。
コンテンツの出力に使用します。

page

JSPページ自身を表します。
実際にはObjectクラスのオブジェクトです。

config

javax.servlet.ServletConfigのオブジェクトです。
JSPページのパラメータの設定に使用します。

exeption

例外発生時にエラー情報を取得します。
使用するためにはPageディレクティブでisErrorPage="true"の指定が必要です。

多くの種類の暗黙オブジェクトがありますが、この中でよく使用するのは「request」と「session」です。

JSPはHTMLにJavaのプログラムを書いて作成しますが、実体はJavaのプログラムです。
JSPのページに対してブラウザからアクセスされた際に、JSPファイルは一度Javaのファイルに変換されます。
そのあとコンパイルされてclassファイルとなり、そのプログラムが実行されることでHTMLファイルが出力され、ブラウザに出力される仕組みです。

JavaでWebアプリケーションを作成する際、プログラムを修正し、それを反映させるためには、一度Webサーバーを再起動させる必要があります。
しかし、JSPファイルはクライアントからアクセスがあった時に変換が行われる仕組みのため、中身を修正してもWebサーバーを再起動させる必要がありません。
JSPファイルを修正した直後でのアクセスでは、一度JSPファイルをJavaファイルに変換し、そのあとコンパイルする作業が必要です。
そのためアクセスに多少時間がかかります。
しかし、2度目以降のアクセスでは、JSPファイルは既にコンパイルされている状態なので、変換の手順を踏まずにプログラムが実行されます。
JSPがどういう仕組みで実行されるのかを知っておきましょう。

実際に変換されたJavaファイルを見ることも可能です。

VS Codeの場合
C:\Users\<ユーザー名>\AppData\Roaming\Code\User\workspaceStorage\<ハッシュ値?>\adashen.vscode-tomcat\tomcat\Tomcat8\work\Catalina\localhost\<フォルダ名>\org\apache\jsp

Eclipseの場合
${workspace}¥.metadata¥.plugins¥org.eclipse.wst.server.core¥tmp0¥work¥Catalina¥localhost¥${project}¥org¥apache¥jsp


JSP内でのメソッドの定義

JSP内でメソッドを定義することも可能です。
その場合、スクリプトレットの開始時に「!」を付けます。

<%!
int add(int n, int m) {
  return n + m;
}
%>
<%-- 呼び出し --%>
<%= add(10, 20) %>

JSP内でメソッドの定義をする場面はそれほど多くはありませんが、知識として知っておきましょう。


画面遷移

JSPを使ってWebアプリを作成する場合、通常は複数のJSPファイルを作成しそれぞれでデータを受け渡ししながら1つのWebアプリを作成していきます。
ここではJSPから別のJSPへ画面遷移する方法と、フォームから入力された値を受け取る方法を見ていきます。

input.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Input</title>
</head>
<body>
<form action="output.jsp">
  <div>何か入力してください。<input type="text" name="repeat"></div>
  <div><button type="submit" value="送信">送信</button></div>
</form>
</body>
</html>

output.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Output</title>
</head>
<body>
<%
    String str = request.getParameter("repeat");
    out.println(str);
%>
<p><a href="input.jsp">戻る</a></p>
</body>
</html>

2つのJSPファイルが作成できたら、ブラウザからinput.jspにアクセスします。
テキストボックスと送信ボタンがあるだけの簡単な画面が表示されます。
テキストボックスに適当に何か入力し、送信ボタンを押すと、output.jspに遷移し、入力した値がそのまま表示されます。

JSPから他のJSPへ画面遷移するには、formタグのaction属性や、aタグのhref属性でjspファイルへのパスを書いてあげればOKです。
フォームからの入力値を受け取るには、requestオブジェクトの、getParameterメソッドを使用します。
メソッドの引数では、受け取りたいinput要素のname属性の値を指定します。
今回の例では、テキストボックスのname属性の値を「repeat」としたので、getParameterメソッドの引数でもrepeatを指定します。

<input type="text" name="repeat">
request.getParameter("repeat");

今回はテキストボックスのみで値を受け取りましたが、他の要素(チェックボックスラジオボタンなど)でも受け取り方は同じです。
getParameterメソッドの引数でname属性の値を指定することで、value属性の値をStringで取得することができます。
例外として、チェックボックスなど、複数の値が取得できる可能性がある要素に関しては、
getParameterValuesメソッドを使用することで、配列として値を受け取ることができます。  

request.getParameterValues("checkBox");

文字化け対策

先ほどのプログラムで、フォームからの送信をGETではなくPOSTに変更してみます。
(method属性を指定しない場合はGETになるので、先ほどまでの例はGETで送信されています。)

<form action="output.jsp" method="POST">
  <div>何か入力してください。<input type="text" name="repeat"></div>
  <div><button type="submit" value="送信">送信</button></div>
</form>

ここで、テキストボックスに日本語の文字を入れて送信ボタンを押下してみます。
結果は、文字化けした文字が表示されるはずです。
これはTomcatの仕様で、POSTで送信されたデータをそのまま受け取ると日本語は文字化けしてしまいます。
POSTデータを受け取る場合requestオブジェクトのsetCharacterEncodingメソッドを使用することで解決することができます。

request.setCharacterEncoding("UTF-8");
String str = request.getParameter("repeat");
out.println(str);

setCharacterEncodingは、データを受け取るときの文字コードを指定するメソッドです。
データを送信するときには文字コードUTF-8で送信されるため、setCharacterEncodingメソッドでUTF-8を指定して上げることで、文字化けを防ぐことができます。
この処理はgetParameterメソッドで値を受け取る前に行う必要があることに注意してください。

ちなみに、GETの場合の文字コードの指定はserver.xmlというファイルで行います。

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1"
redirectPort="8443" URIEncoding="UTF-8">

ただし、デフォルトでUTF-8の設定になっているため、通常は設定しなくても文字化けは起きません。

文字化けが発生した場合には、文字コードの設定がどのようになっているかを確認してみましょう。


スコープ

スコープとは範囲という意味を表す言葉です。
変数が有効な範囲を変数のスコープと呼んだりします。
Webアプリケーションにおけるスコープは、APサーバーに値を保存しておく期間のことです。
Tomcatでは4つのスコープを扱うことができます。

  • ページスコープ
  • リクエストスコープ
  • セッションスコープ
  • アプリケーションスコープ

ページスコープが最も保持される期間が短く、アプリケーションスコープが最も期間がなくなります。
4つのスコープがありますが、実際によく使用されるのはリクエストスコープとセッションスコープで、ページスコープとアプリケーションスコープはほとんど使用されません。

以下のソースでそれぞれのソースの動作を確認してみましょう。

scope.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>スコープの確認</title>
</head>
<body>
<%
// pageContext.removeAttribute("num");
// request.removeAttribute("num");
// session.removeAttribute("num");
// application.removeAttribute("num");

//////////////////////////////
// 1 ページスコープ
//////////////////////////////
// ページスコープから値を取得する
Integer ps = (Integer) pageContext.getAttribute("num");
// null対処
if (ps == null) {
    ps = 1;
}
ps++;
out.println("ページスコープ:" + ps + "<br>");
// ページスコープに値をセットする
pageContext.setAttribute("num", ps);

//////////////////////////////
// 2 リクエストスコープ
//////////////////////////////
// リクエストスコープから値を取得
Integer rs = (Integer) request.getAttribute("num");
// null対処
if (rs == null) {
    rs = 1;
}
rs++;
out.println("リクエストスコープ:" + rs + "<br>");
// リクエストスコープに値をセット
request.setAttribute("num", rs);

//////////////////////////////
// 3 セッションスコープ
//////////////////////////////
// セッションスコープから値をセット
Integer ss = (Integer) session.getAttribute("num");
// null対処
if (ss == null) {
    ss = 1;
}
ss++;
out.println("セッションスコープ:" + ss + "<br>");
// セッションスコープに値をセットする
session.setAttribute("num", ss);

//////////////////////////////
// 4 アプリケーションスコープ
//////////////////////////////
// アプリケーションから値を取得
Integer as = (Integer) application.getAttribute("num");
// null対処
if (as == null) {
    as = 1;
}
as++;
out.println("アプリケーションスコープ:" + as + "<br>");
// アプリケーションスコープに値をセット
application.setAttribute("num", as);
%>
<form action="scope.jsp">
  <button type="submit">カウントアップ</button>
</form>
</body>
</html>

結果

ページスコープ:1
リクエストスコープ:1
セッションスコープ:1
アプリケーションスコープ:1
カウントアップ(ボタン)

最初は全てのスコープが1の状態になります。
カウントアップのボタンを押すと、それぞれのスコープに1を足す処理となっています。
動作を確認すると、ページスコープとリクエストスコープはずっと1のままになっているはずです。
一方で、セッションスコープとアプリケーションスコープはボタンを押すたびに1ずつ増えていきます。

ページスコープとリクエストスコープ

上記のプログラムではページスコープとリクエストスコープの違いは分かりません。

ページスコープは、JSPページ内で有効となります。
ページスコープはあまり使われないと話しましたが、ページスコープでできることはローカル変数でできることと変わらないからです。
ページ内だけで使用する値を保存するだけなら、ローカル変数でも対応できるため、ページスコープはあまり使われていません。

リクエストスコープはリクエスト値が有効で、レスポンスされると値は消滅します。
JSPだけでプログラムを作成しているとリクエストスコープを使用するメリットは分かりにくいですが、サーブレットなどの技術と組み合わせてシステムを作成する際にはよく利用されます。
例えば、検索フォームにキーワードを入力して検索ボタンを押した場合などです。
このとき、検索キーワードがプログラムに送信され、データベースから該当するデータを取得してきます。
取得してきたデータはリクエストスコープにセットしておくことで、JSPでは検索結果を表示することが可能です。

セッションスコープとアプリケーションスコープ

セッションスコープとアプリケーションスコープもこのプログラムでは違いが分かりにくいかもしれません。
異なるブラウザで動作確認すると違いが分かりやすくなります。
Google Chromeで確認しつつ、Edgeなど、別のブラウザを使用して同じプログラムを動かしてみてください。

セッションスコープの値はブラウザごとに1からカウントされます。
アプリケーションスコープは違うブラウザを使っても値が共有されているはずです。

アプリケーションスコープは、サーバーで1つだけ存在し、複数のクライアント間で共有されます。
アプリケーションスコープはサーバーを停止したり、再起動したりすると値はなくなります。
そのため、永続的にデータを保存しておきたい場合にはアプリケーションスコープを使うのは向いていません。
永続的にデータを保存する場合は、DB(データベース)を使用することがほとんどです。
そのためアプリケーションスコープが使用される場面は限られます。

セッションスコープはクライアント(Webブラウザ)単位で値を保持するスコープです。
つまりクライアントの数だけ存在します。

値の取得と設定方法

ここでスコープからの値の取得方法と値の設定方法について解説しておきます。

// スコープへの値のセット
スコープのオブジェクト.setAttribute("key", 値);
// スコープからの値の取得
// 値を取得する際にはキャストする
型 変数 = (型) スコープのオブジェクト.getAttribute("key");
// 値の破棄
スコープのオブジェクト.removeAttribute("key");

値の取得とセット、それぞれで使用するメソッドはどのスコープでも同じです。
値のセットにはsetAttributeメソッドを使用します。
値の取得にはgetAttributeメソッドを使用します。
スコープのオブジェクトは、用途に応じてpageContext, request, session, applicationのいずれかを使用します。

setAttributeの第一引数とgetAttributeの引数には任意の文字列を指定できます。
どんな文字列でもかまいませんが、値のセットと取得側で同じ文字列である必要があります。
一般になんの値を扱っているのかが分かるような名前にします。

setAttributeメソッドの第二引数にはObject型を受け取ります。
つまり、任意の型の値を設定できます。
その代わり、値を取得する際にはキャストして使用したい型に戻してあげる必要があります。

値を意図的に破棄したい場合はremoveAttributeメソッドを使用することで破棄することができます。

セッションスコープについて

セッションスコープについてはもう少し詳しく説明します。

セッションスコープはクライアント単位で値を保持しておくことができ、非常に便利です。
しかし、クライアントが増えるとその分だけスコープができるため、使い方によってはサーバーのメモリ容量を圧迫して、プログラムのパフォーマンスに影響を与えてしまう可能性もあります。
そのため、リクエストスコープでやり取りできる値に関してはできる限りリクエストスコープでやり取りするなどの工夫が必要です。

セッションはクライアントの数だけ作成されますが、ログアウトしたユーザーや一定時間操作がされていないセッションの情報をずっと保持していれば、メモリを無駄に使用することになります。
多くのAPサーバーでは一定の時間操作がなかった場合には自動的にセッションを削除するようになっており、Tomcatではデフォルトで30分でセッションを削除するようになっています。
この設定はWeb.xmlという設定ファイルで変更することが可能です。
また、セッションはremoveAttributeメソッドやinvalidateメソッドを使用して明示的にデータを削除することも可能です。
ログアウトした際など、不要なセッションは削除するように処理しておくことが大切です。

セッションを識別する仕組み

セッションはクライアントの数だけ存在します。
つまり、10人が同時にそのプログラムを利用している場合は、10個のセッションが存在することになります。
この時、どのクライアントの情報がどのセッション情報なのかを識別する仕組みが必要になります。

この時に使用されるのがCookieと呼ばれる技術です。 Cookieはクライアント(Webブラウザ)に保存されるテキストデータです。
APサーバー(Tomcat)は、セッションを作成するとき、同時にセッションIDと呼ばれる値(英数字の列)を生成します。
そして、レスポンスを返す際にこのセッションIDをクライアントのCookieにセットします。
クライアントは同じWebサーバーにリクエストを送信する際に、Cookieに保存されたセッションIDも同時に送信します。

APサーバーはクライアントから送られてきたセッションIDを元に、紐づいているセッションを識別します。

Cookieの仕組みは便利ですがいくつか注意点もあります。
Cookieはブラウザに保存されているテキストデータなので、ブラウザの操作によって削除することも可能です。
Webサービスを利用している最中にCookieのデータを削除すると、セッションIDも削除されるため、ログインしていた状態が失われてしまう可能性があります。
また、セッションIDが不正に第三者に知られてしまった場合、不正ログインされてしまうなど、セキュリティ面にも注意が必要です。


Servlet

Javaの技術を使ってWebアプリケーションを作成する際、JSPを使うことでWebアプリケーションの作成が可能です。
しかし、作成するプログラムの規模が大きくなってくると、JSPだけでプログラムを作成するには限界が出てきます。
JSPを使うことで、HTMLの中にJavaのプログラムが書けるようになります。
しかし、元々HTMLとはJavaは全く異なる技術なので、文法も違えばできることも違います。
そのため、1つのファイルの中にHTMLとJavaのプログラムをたくさん書いていると、ソースコードが読みづらくなるうえに、修正も困難になってきます。
複数人で開発を行う場合には、HTMLの知識はけどJavaの知識がない人もいれば、逆にJavaの知識はあるけどHTMLの知識がない人もいるかもしれません。
そうなった場合、JSPを使用していると、複数人が同じファイルを修正することになり、開発の効率が落ちます。
画面側のファイルとJavaによる処理をファイルを分割して作成できるようになると便利です。

JavaEEではサーブレットと呼ばれる技術でこのような仕組みをサポートします。
サーブレットを使用すると、JavaのプログラムでHTMLを出力したり、フォームからの入力を受け取って処理を行ったり、別の画面への画面遷移を行ったりすることができます。

まずはサーブレットの例として以下のプログラムを確認してください。

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

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

// URLマッピングを表すアノテーション
@WebServlet("/TestServlet")
// HttpServletクラスを継承したクラスがサーブレットのクラスとなる
public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // コンストラクタ
    public TestServlet() {
        super();
    }

  // GETでアクセスしたときに呼ばれるメソッド
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>");
        out.println("Hello World!");
        out.println("</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<p>Hello Servlet!</p>");
        out.println("</body>");
        out.println("</html>");

    }

  // POSTでアクセスしたときに呼ばれるメソッド
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

ファイルが作成できたら、Tomcatを起動し、ブラウザから
http://localhost:8080/{プロジェクト名}/TestServlet
にアクセスすると、ブラウザに
「Hello Servlet!」が表示されます。

解説

特徴

サーブレットクラスは、HttpServletクラスを継承しています。
クラスを作成する際にHttpServletクラスを継承したクラスを作成して上げれば、それがサーブレットになります。
サーブレットは、プログラムの中で自分でインスタンスを作成する(newする)ことはしません。
Tomcatを起動する際に、Tomcatが自動的にインスタンスを作成する仕組みになっています。
そのため、サーブレットに定義されているメソッドも基本的にはから呼び出すことはしません。
ブラウザからGETかPOSTによるリクエストが送られてきたとき、URLに対応したサーブレットのdoGetメソッドdoPostメソッドが呼ばれる仕組みになっています。

WebServletアノテーション

サーブレットのクラス名の上に「@WebServlet」というアノテーションがあります。
これはUMLマッピングと呼ばれ、リクエストのURLによって呼ばれるサーブレットのクラスを紐づける仕組みです。
例えば、アノテーション

@WebServlet("/test")

となっていた場合、URLでは http://localhost:8080/{プロジェクト名}/test
にアクセスしたときのそのサーブレットが動作するようになります。

1つのサーブレットで複数のURLに対応させることも可能です。
その場合、{}(中かっこ)の中にカンマ区切りで複数のマッピングを書きます。

@WebServlet({"/testServlet", "/test"})

こうすると、URLでは
http://localhost:8080/{プロジェクト名}/test
http://localhost:8080/{プロジェクト名}/testServlet
の両方に対応することができます。

また、ワイルドカードを指定することもかのうです。

@WebServlet("/test/*")

このようにした場合、 http://localhost:8080/{プロジェクト名}/test
http://localhost:8080/{プロジェクト名}/test/abc
http://localhost:8080/{プロジェクト名}/test/abc/1
など、testのあとに任意の文字が続いても呼ばれるようになります。

JSPとの違い

JSPファイルは、リクエストを受け取った時にJavaソースコードに変換され、コンパイルされるという仕組みでした。
そのため、APサーバー(Tomcat)が起動したままの状態でJSPファイルを修正しても、サーバーを再起動することなく修正が反映されます。

一方サーブレットTomcatが起動するときにインスタンスを作成し、サーバーが停止するまでの間そのインスタンスが使用されます。
そのため、サーバーが起動した状態のままでサーブレットのファイルの中身を修正しても、その変更は反映されません。
サーブレットの変更内容を反映させるにはサーバーを再起動させる必要があります。

先の例ではサーブレットでHTMLを出力しており、JSPと実質同じことが実現できていますが、中の仕組みには違いがあることを知っておきましょう。

また、注意点として、複数のサーブレットクラスを作成した際に、そのクラス同士でURLマッピングの内容が重複している場合、サーバー起動時にエラー発生します。
これは、URLマッピングが重複するとリクエストが送られてきた際にどのサーブレットが対応すればよいか分からなくなるためです。
サーブレットを作成していてサーバーの起動でエラーが発生した場合には、URLマッピングに重複がないかどうかを確認してみましょう。

サーブレットクラスのメソッド

サーブレットクラスがどのようなメソッドを保持しているかはHttpServletのリファレンスを確認してください。
自作のサーブレットでは、HTTPリクエストのメソッド(GET, POST, PUT, DELETEのいずれか)に対応したメソッドをオーバーライドします。
ほとんどの場合doGetかdoPostのいずれか(あるいは両方)をオーバーライドし、リクエストが来た時の処理を実装します。

サーブレットインスタンス化されたときに呼ばれるinitメソッドや、インスタンスが破棄されるときに呼ばれるdestoryメソッドをオーバーライドすることも可能です。

Web.xml

Tomcatのバージョン8以降(Servlet3.0以降)では、アノテーションによるURLマッピングが可能となっていますが、それ以前のバージョンでは、設定ファイルによりURLマッピングを行っていました。
Tomcatの設定ファイルはweb.xmlというファイルです。
web.xmlはWEB-INFの直下に作成します。

以下はバージョン2.5での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" id="WebApp_ID" version="2.5">
  <servlet>
    <servlet-name>Foo</servlet-name>
    <servlet-class>FooServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Foo</servlet-name>
    <url-pattern>/FooServlet</url-pattern>
    <url-pattern>/foo</url-pattern>
  </servlet-mapping>
</web-app>

servlet-class要素でサーブレットのクラス名を指定します。
servlet-name要素ではそのクラス名に対する識別子(あだ名のようなのも。何でもよい)を指定します。
servlet-mapping要素では、servlet要素のservlet-name要素で指定した識別子を指定し、サーブレットに対するURLマッピングを指定します。

サーブレットのバージョン3.0以降でも、Web.xmlによるURLマッピングも可能です。
その際、アノテーションによるマッピングと重複してしまった場合にはサーバーの起動時にエラーが発生するので注意が必要です。

また、設定ファイルはTomcatの起動時に読み込まれるため、内容を修正した場合はサーバーの再起動が必要になります。

Web.xml自体はTomcatの設定ファイルなので、URLマッピング以外にも様々な設定を行うことが可能です。


JSPサーブレットの連携

先の例ではサーブレットを使ってHTMLの出力を行いました。
しかし、実際の開発ではサーブレットでHTMLの出力はあまり行いません。
HTMLを作成するはサーブレットよりもJSPで行う方が分かりやすいです。
実際の開発では画面への表示に関する部分はJSPを使用し、サーブレットでは値を受け取って処理し、画面遷移などを行います。

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>ログイン</title>
  </head>
  <body>
    <form action="login" method="post">
    <div>ID: <input type="text" name="id"></div>
    <div>PASS: <input type="password" name="password"></div>
    <div><button type="submit">ログイン</button></div>
    </form>
  </body>
</html>

mypage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String id = (String) request.getAttribute("id");
%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>マイページ</title>
  </head>
  <body>
    <div>
     <p>ようこそ <%= id %> さん </p>
    </div>
    </body>
</html>

LogninServlet.java

package servlet;

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("/login")
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public LoginServlet() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // login.jspに画面遷移する
        request.getRequestDispatcher("/login.jsp").forward(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 文字コードの設定
    request.setCharacterEncoding("UTF-8");
    // フォームからの入力値を受け取る
        String id = request.getParameter("id");
        String pass = request.getParameter("password");

    // 入力値によって画面の遷移先を変える
        if (pass.equals("admin")) {
            request.setAttribute("id", id);
            request.getRequestDispatcher("/mypage.jsp").forward(request, response);
        } else {
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
}

解説

プログラムの流れ

このプログラムでは、まずTomcatを起動した後、ブラウザから
https://localhost:8080/{プロジェクト名}/login
にアクセスします。
そうするとLoginServletのdoGetメソッドが実行されます。
(URLを直接指定してアクセスした場合はGETによるアクセスになります。)
doGetメソッドでは、login.jspフォワード(画面遷移)する処理が行われ、login.jspが表示されます。
そこでIDとPASSを適当に入力して「ログイン」ボタンを押下すると、今度はLoginServletのdoPostメソッドが呼ばれます。
入力したパスワードが「admin」だった場合には、mypage.jspに遷移し、違う場合にはlogin.jspに戻るような処理になっています。

サーブレット経由のアクセス

ブラウザからURLを直接入力した場合、GETメソッドによるアクセスとなるため、URLマッピングに対応したサーブレットのdoGetメソッドが呼ばれることになります。
サーブレットを使用せずJSPだけを使用した場合、ブラウザからはJSPのファイル名を直接指定していました。
しかし、画面の数が多くなってくると、サーブレット経由でJSPを表示させるようにするのが一般的です。

action属性の指定

JSPでフォームボタンが押されたときにサーブレットの処理が呼ばれるようにするには、action属性の値をサーブレットのURLマッピングと紐づけるようにします。

GETとPOST

ここではGETとPOSTを使い分けて画面遷移を行っていますが、あたらめてGETとPOSTの使い分けについて解説しておきます。
ブラウザからURLを直接入力してアクセスした際には、GETメソッドによるアクセスになります。
そのため、サーブレットではメソッドが呼ばれます。
また、リンク(aタグ)による画面遷移でもGETメソッドでのアクセスになります。
一方、フォームでサブミットボタンを押下した際には、formタグのmethod属性の値によってGETかPOSTに分かれます。

一般には、検索のような、情報の取得に関する処理ではGETが使用されます。
ユーザーを登録する場合や、記事を投稿する場合など、データの更新が行われる処理ではPOSTが使用されます。
データの更新を行わない場合でも、ログインする場合など、個人情報に関するデータを送信する場合にはPOSTが使用されます。

GETのPOSTの違いは、パラメータの送信のされ方です。
GETの場合、URLにパラメータが付加されます。
Googleなどで適当にキーワードを入力して検索を実行すると、URLの後ろに
?q=検索キーワード&...
のような情報が付加されているかと思います。
GETで情報を送信した場合、URLは
http://ホスト名/パス?パラメータ1=値&ぱためーた2=値
という風に、「?」マークの後で「&」区切りで「パラメータ=値」の情報が付きます。

URLにパラメータがつくことにメリットは、そのURLを共有することで、検索結果のページなどをそのまま共有できることです。
ECサイトで欲しい商品を検索して、その詳細のページを共有したい時、URLを共有することでそのまま同じページを見てもらうことができます。

POSTの場合、フォームから入力した情報はURLには付随されません。
HTTPリクエストのボディ部の情報として送信されます。
データの更新処理などがある場合は基本的にPOSTを使用しますが、仮にURLにパラメータの情報が追加されるとしたら、URLを直接入力してアクセスすることで、不正にデータを更新されてしまう可能性が出てきます。
また、ログインする際、ユーザー名とパスワードがURLに追加されてしまうと、他の人から簡単にパスワードを除き見られてしまう可能性があります。
そういった理由から、データの更新を行う場合と、個人情報を送信する場合はPOSTが使用されます。

GETとPOSTの特徴は、Javaで開発する場合に限らず、Webアプリケーションを開発するにあたって必須となる知識です。
GETとPOSTの特徴を知って、適切に使い分けられるようにしておきましょう。

WEB-INF

先の例ではサーブレット経由でJSPファイルにアクセスするようにしましたが、実はURLで直接JSPファイルを指定することも可能となっています。
ブラウザから https://localhost:8080/{プロジェクト名}/login.jsp
https://localhost:8080/{プロジェクト名}/mypage.jsp
とそれぞれアクセスすると、それぞれのページが表示されます。

しかし、これは本来はよくない状況です。
login.jspについては特に問題はありませんが、mypage.jspは、本来ログインが成功した場合に表示させたいページのはずです。
ですが、URLを直接入力することによってアクセスできてしまうと、ログインしていないにも関わらず、正規にログインした人と同じ操作ができてしまいます。

このような状況を防ぐために、JSPファイルに直接アクセスできないようにする仕組みがあります。
それは、JSPファイルを「WEB-INF」フォルダ配下に入れることです。
WEB-INF配下に入れたファイルは、ブラウザからURLを直接入力してもアクセスできないようになります。
サーブレットからそのJSPファイルに遷移するには、WEB-INFも含めたパスを指定します。

request.getRequestDispatcher("/WEB-INF/mypage.jsp").forward(request, response);

セッションの利用

Webアプリケーションを作成するにあたって、セッションを利用する場面は多くあります。
サーブレットでもセッションを利用することは多くあります。
JSPの場合、暗黙オブジェクトの中に「session」というオブジェクトがあったため、特に意識せずにセッションを利用することができます。
サーブレットの場合、セッションを利用するにはセッションのオブジェクトを取得する必要があります。
(doGetやdoPostメソッドの引数はリクエストとレスポンスのオブジェクトしかないため、セッションのオブジェクトは別で取得する必要があります。)
セッションを取得するにはリクエストオブジェクトのgetSessionメソッドを利用します。

// セッションの取得
HttpSession session = request.getSession();

フィルター

POSTでデータを受け取る際には、毎回setCharacterEncodingメソッドを使用して文字コードを設定してあげる必要があります。

ログインして利用するようなシステムの場合、一般にはログイン画面でIDとパスワードを入力してもらいます。
そしてログインに成功した場合には、ユーザー情報をセッションに格納します。
しかし、セッションでは一定時間の間操作がなければ、自動的にセッション情報が削除されます。(セッションタイムアウト)
そうなった場合、一度ログイン画面に戻り、再度ログイン操作をしてもらう必要があります。

このような文字コードの設定や、セッションのデータを保持しているかどうかの設定などは、画面遷移の度に処理する必要がありますが、それぞれのサーブレットJSPに毎回同じ処理を書くのは非効率です。
こういう場合、「フィルター」という機能を利用すると便利です。
フィルターを利用すると、リクエストが送信されてからサーブレットの処理が行われる前と、サーブレットの処理が行われてからレスポンスが返る前に処理を挟むことが可能です。

処理の流れとしては、
リクエスト ⇒ フィルター ⇒ サーブレット ⇒ フィルター レスポンス
の流れになります。

SampleFilter.java

package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

@WebFilter("/*")
public class SampleFilter implements Filter {

  // コンストラクタ
  public SampleFilter() {
  }

  // 後処理
    public void destroy() {
    }

  // フィルター処理
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 文字コードの設定
      request.setCharacterEncoding("UTF-8");
    // 次のフィルターやサーブレットの処理へ移る
        chain.doFilter(request, response);
    }

  // 初期化処理
    public void init(FilterConfig fConfig) throws ServletException {
    }
}

このフィルターの例では文字コードの設定を行っています。
これにより、サーブレットで毎回文字コードの設定を行う必要がなくなります。
フィルターのクラスを作成するには、Filterインターフェースを実装する必要があります。
@WebFilterアノテーションでURLマッピングを指定することで、URLがマッチするリクエストの場合のみフィルターを適用することができます。


EL式・JSTL

JSPサーブレットでうまく役割分担してプログラムを作成すれば、JSP内でスクリプトレットの記述を最小限にして開発をすることができます。
しかし、スクリプトレットの記述が少しでも必要になると、少なからずJavaの知識が必要になってきます。
本来、画面の部分はHTMLやCSSに詳しいデザイナーの人に任せ、Javaのプログラムの部分はプログラマーの人に任せた方が効率よく開発ができます。
しかし、セッションスコープやリクエストスコープのの値を出力する処理をするためだけにJavaの知識を求めるのは効率的ではありません。

そこで、JSP内でスクリプトレットを使用せずに簡単な値の出力や演算を行うための技術としてEL式と呼ばれるものがあります。
また、Javaの文法を知らなくても、独自のタグを使用することで簡単な処理をできるようにしたカスタムタグと呼ばれる技術もあります。

EL式

まずはEL式について見ていきます。
EL式はExpression Language の略です。
サーブレットの例で作成したmypage.jspを以下の様に修正します。

mypage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>マイページ</title>
  </head>
  <body>
    <div>
      <p>ようこそ ${requestScode.id} さん </p>
    </div>
  </body>
</html>

結果は変わらないはずです。
EL式では、「${}」 を使用します。
{}(中かっこ)の中の式が評価され、その結果がブラウザ上にそのまま出力されます。
requestScodeはEL式の中で使用することができるオブジェクトで、リクエストスコープを表します。

EL式の中で扱える式には以下のものがあります。 * pageScope:ページスコープを表す * requestScode:リクエストスコープを表す * sessionScope:セッションスコープを表す * applicationScope:アプリケーションスコープを表す * param:パラメータのキーと値(String)を管理する * paramValues:パラメータとキーの値(Stringの配列)を管理する * cookie:クッキーのキーと値を管理する * header:リクエストヘッダーのキーと値(String)を管理する * headerValues:リクエストヘッダーのキーと値(String[])を管理する * initParam:初期化パラメーターのキーと値を管理する * pageContext:JSPのオブジェクトを管理する

ちなみに、「requestScode」は省略して

${id}

のように書くこともできます。
オブジェクトを省略した場合、
ページスコープ ⇒ リクエストスコープ ⇒ セッションスコープ ⇒ アプリケーションスコープ
の順にスコープを参照し、キーが見つかったスコープのValueが出力される仕組みです。

また、EL式では算術演算子、比較演算子、論理演算子が使用可能です。

算術演算子

  • +:加算
  • -:減算
  • *:乗算
  • /(div):除算
  • %(mod):剰余

比較演算子

  • ==(eq):等しい
  • !=(ne):等しくない
  • <(lt):より小さい
  • <=(le):以下
  • >(gt):より大きい
  • >=(ge):以上
  • empty:空またはnull

論理演算子

  • &&(and)
  • ||(or)
  • !(not)

三項演算子

  • a ? b : c
${10 + 20}
${10 == 10}
${10 != 10}

JSPの中でこのように書くと、それぞれ「30」「true」「false」と出力されます。

比較演算子は、この後に紹介するJSTLと組み合わせて使用されることもあります。

JSTL

JSTLはJavaServer Pages Standard Tag Library の略です。
JSPでは、プログラマが自由にタグを追加できるカスタムタグと呼ばれる仕組みが用意されています。
そこでよく作成されていたタグを標準化してまとめられたものがJSTLです。

JSTLJavaのライブラリのため、使用するにはjarファイルが必要です。
以下のページそれぞれjarファイルをダウンロードします。
https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api/1.2
https://mvnrepository.com/artifact/org.glassfish.web/jstl-impl/1.2

ダウンロードしたら、「jstl-impl-1.2.jar」「jstl-api-1.2.jar」を、「WEB-INF/lib」配下にそれぞれ配置します。
その後サーバーを再起動させれば、jstlを使用することができます。

まずは例を見てみます。
mypage.jspを以下の様に修正します。

mypage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>マイページ</title>
  </head>
  <body>
    <div>
      <c:choose>
        <c:when test="${id == 'admin'}">
          <p>管理者です。</p>
        </c:when>
        <c:otherwise>
          <p>一般ユーザーです。</p>
        </c:otherwise>
      </c:choose>
      <p>ようこそ ${id} さん </p>
    </div>
  </body>
</html>

この画面では、login.jspでIDに「admin」を入力した場合、「管理者です。」と表示されます。
それ以外の値を入力した場合には、「一般ユーザーです。」と表示されます。

JSTLを使用するにはまず先頭にtaglibの宣言を記述する必要があります。
JSTLを使用するにはjarファイル + taglib宣言が必要だと覚えておきましょう。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

以下の部分がJSTLのカスタムタグを使って処理を記述したものです。

<c:choose>
  <c:when test="${id == 'admin'}">
    <p>管理者です。</p>
  </c:when>
  <c:otherwise>
    <p>一般ユーザーです。</p>
  </c:otherwise>
</c:choose>

タグの先頭にある「c」は、taglib宣言のprefix属性で指定したものです。
ここではcoreタグライブラリを宣言しているのですが、coreタグライブラリではprefixとして「c」がよく使用されます。

coreタグライブラリには他にも以下のようなものがあります。

  • set:スコープに値をセットする
  • remove:スコープの値を削除する
  • if:elseがない単純な条件分岐
  • choose, when, otherwise:複数の条件分岐
  • forEach:繰り返し
  • out:値の出力

また、JSTLにはcoreタグライブラリ以外にも以下のようなライブラリがあります。

種類 説明 URI prefix
core 分岐や繰り返しなどの基本的な操作を提供 http://java.sun.com/jsp/jstl/core c
function 主に文字列操作を提供 http://java.sun.com/jsp/jstl/functions fn
il8n 国際化対応の機能を提供 http://java.sun.com/jsp/jstl/fmt fmt
xml XML操作を提供 http://java.sun.com/jsp/jstl/xml x
database DB操作を提供 http://java.sun.com/jsp/jstl/sql sql

Javaに慣れている人であれば、JSTLを見た時、Javaで書いた方が簡単だと感じるかもしれません。
しかし、HTMLの知識があってJavaの知識がないデザイナーの人からすれば、スクリプトレットで書く方がハードルが高いです。
スクリプトレットを書くためには、ある程度Javaの知識が必要になってきます。
しかし、JSTLであれば、Javaの知識がなくとも、新しいタグの使い方さえ覚えれば使うことができます。
そのため役割分担がしやすくなります。

JSPではEL式やJSTLを活用して、Javaのコードを書かなくても済むようにしていきましょう。

XSS対策

login.jspでIDの入力欄に

<strong>admin</strong>

のように、HTMLタグを付けて入力してみましょう。
そうすると、HTMLタグとして認識され、出力されたときに「admin」が太文字になって表示されます。

これは本来は意図しない動作です。
strongタグでは表示が太文字になるだけなので、大きな問題にはなりませんが、使い方によってはセキュリティ上の問題につながる場合があります。
例えば、scriptタグを使って悪意のあるプログラムを埋め込んだり、iframeタグを使って別のページを埋め込んで悪意あるページに誘導させる、といったことが可能となります。
このようにフォームの入力を利用してブラウザの意図しない表示をさせる攻撃をクロスサイトスクリプティング(XSS)といいます。

Webアプリケーションを作成する場合はXSS対策が必須です。
JSPの場合、JSTLで提供されているタグを利用することで対策が可能です。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>マイページ</title>
  </head>
  <body>
    <div>
      <c:choose>
        <c:when test="${id == 'admin'}">
          <p>管理者です。</p>
        </c:when>
        <c:otherwise>
          <p>一般ユーザーです。</p>
        </c:otherwise>
      </c:choose>
     <p>ようこそ ${fn:escapeXml(id)} さん </p>
    </div>
    </body>
</html>

functionタグライブラリを宣言し、escapeXmlを使用することで入力した文字がエスケープされ、HTMLタグではなく文字として処理されます。
Javaに限らず、どの言語であってもWebアプリケーションを作成するときはエスケープ処理が必要になることを覚えておきましょう。


MVCモデル

MVCモデルとは、Webアプリケーションを作成する際によく使用される設計手法です。
Javaの開発だけで使われる用語ではなく、PHPなど、他の言語での開発でもよく用いられる設計モデルです。
MVCは、MはModel、VはView、CはContollerの略です。

V(View)は。見た目の部分のことです。
JSP/サーブレットの場合、JSPがViewに該当します。

M(Model)は、Webアプリケーションの処理を担う部分です。
例えば、DB(データベース)からデータを取得する処理や、データを登録する処理、複雑な計算をする処理などが該当します。
JSP/Servletの技術を使ってWebアプリケーションを作成する場合、JSPサーブレット以外で自分で作成したクラスはModelに該当すると考えて良いでしょう。

C(Controller)は、ViewとModelの橋渡しを担う部分です。
Viewから受け取った入力値をModelに渡して処理を行います。
そしてModelでの処理結果を受け取り、表示するためにViewに渡しす処理などがControllerの役割です。
JSP/ServletではServletの部分がControllerになります。

ここではMVCモデルに沿って作った簡単なプログラムの例を紹介します。
5つのファイルを作成します。

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>トップページ</title>
  </head>
  <body>
    <a href="userList">ユーザー一覧</a>
  </body>
</html>

user_list.jsp

<%@ page<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>ユーザー一覧</title>
  </head>
  <body>
    <table border="1">
      <tr>
        <th>名前</th><th>年齢</th><th>メールアドレス</th>
      </tr>
      <c:forEach var="user" items="${userList}">
      <tr>
        <td>${fn:escapeXml(user.name)}</td>
        <td>${fn:escapeXml(user.age)}</td>
        <td>${fn:escapeXml(user.mail)}</td>
      </tr>
      </c:forEach>
    </table>
  </body>
</html>

UserServlet.java

package servlet;

import java.io.IOException;
import java.util.List;

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

import Logic.UserLogic;
import beans.User;

@WebServlet("/userList")
public class UserServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public UserServlet() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        UserLogic logic = new UserLogic();
        List<User> list = logic.getUserList();

        request.setAttribute("userList", list);

        request.getRequestDispatcher("/user_list.jsp").forward(request, response);
    }
}

UserLogic.java

package Logic;

import java.util.ArrayList;
import java.util.List;

import beans.User;

public class UserLogic {

    public List<User> getUserList() {

        List<User> list = new ArrayList<>();
        list.add(new User("Aさん", 25, "aaa@xxxx.com"));
        list.add(new User("Bさん", 24, "bbb@xxxx.com"));
        list.add(new User("Cさん", 26, "ccc@xxxx.com"));
        list.add(new User("Dさん", 20, "ddd@xxxx.com"));
        list.add(new User("Eさん", 29, "eee@xxxx.com"));

        return list;
    }
}

User.java

package beans;

// ユーザーの情報を格納するクラス
public class User {

    private String name;
    private int age;
    private String mail;

    public User(String name, int age, String mail) {
        this.name = name;
        this.age = age;
        this.mail = mail;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getMail() {
        return mail;
    }
    public void setMail(String mail) {
        this.mail = mail;
    }
}

5つのファイルが作成できたら、ブラウザからindex.jspにアクセスします。
「ユーザー一覧」というリンクだけが表示される単純なページです。
このリンクを押すと、ユーザー一覧ページに遷移し、ユーザーの情報が出力されるプログラムです。

リンクを押したときにはまずサーブレットが呼ばれます。
サーブレットでは、UserLogicクラスのインスタンスを作成し、getUserListの処理を実行し、その結果をリクエストスコープにセットします。
UserLogicクラスでは、Userの一覧を取得し、それぞれのユーザーの情報をUserクラスのインスタンスとして作成し、Listに詰めます。
本来はユーザー情報はDB(データベース)などに格納されており、そこから取得するのが理想ですが、ここでは説明を簡単にするために、UserLogicクラスで直接ユーザーを作成します。

user_listではJSTLのforEachタグを利用してListの要素数だけ出力します。
forEachでは、items属性でリストや配列などのコレクションを指定すると、各要素がvarで指定した変数に格納される仕組みになっています。
そのためvarで指定した「user」にUserクラスのインスタンスが格納されます。
EL式ではuser.name, user.age, user.mailを指定することで、各フィールドから値を取得しています。
しかし、Userクラスでは各フィールドのアクセス修飾子はprivateになっている点に注意してください。
privateでは自クラス以外からアクセスできないため、本来であればuser.nameとすることでnameフィールドにアクセスすることはできないはずです。

実は、EL式ではフィールドにアクセスすると、そのフィールドに対するゲッターメソッドが自動的に呼ばれる仕組みになっています。
なので、フィールドに対するゲッターメソッドが存在しなかったり、スペルが間違っていたり、Java命名規則にのっとっていなかった場合はうまくいかなくなりますので注意してください。

ここでは簡単な例でしたが、MVCによって役割を分けて作成することで、プログラムが作成しやすくなったことを感じ取れればOKです。

参考図書