この記事ではJavaで作成したWebアプリケーションを動作させる際に、URLパスと実ファイルを結びつける「マッピング」というものについて説明する。
目次
マッピングとは
マッピングとは、Webコンテナがサーブレットを選んでクライアントのリクエストを渡すためのものである。
下の図が示すように、クライアントのリクエストは、URLパスを含んでいる。さらに、このURLパスは、コンテキストルートとURLパターンを持っている。この二つの情報からサーブレットは選択され、クライアントからのリクエストが渡される。
サーブレット選択のためのURLパスの使用
どのようにWebコンテナがURLパスからサーブレットを選ぶかを見ていこう。まず、コンテキストルートを選択するところから始まる。サーブレットが配置されているディレクトリの一番上にあるのがコンテキストルートである。
このコンテキストルートが見つかったら、次に以下の順序でWebコンテナはURLパターンからサーブレットを選ぶ。
- URLパターンが正確に一致する。
- URLパターンがパスマッピングで一致する。
- URLパターンが拡張子マッピングで一致する。
- どのURLパターンにも一致しない。
Webコンテナは、大文字と小文字を区別してマッチングのための文字列比較を行う。この時、どのようにそのURLパスの中のURLパターンとサーブレットのURLパターンが比較されるかを、サーブレットを選ぶ順序で見ていこう。
これからの説明で使われるサンプルに登場するサーブレットは次の配置になっている。
URLパターンの正確な一致
URLパターンが正確に一致するとは、URLパスの中のURLパターンとサーブレットのURLパターンの一文字一文字が正確に一致しているということである。
例を見てみよう。URLパスが次のようであれば、URLパターンは「/juice」となる。
http://localhost:8080/Servlet/juice
このURLパターンに一致するのは「/juice」だけである。他のどんな文字列も不一致となる。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/juice | /juice | ○ | ○ |
/JuiCe | × | × | |
/juice以外のすべてのパターン | × | × |
URLパスの「/juice」と正確に一致するURLパターンをアノテーションとweb.xmlで宣言すると次のようになる。
アノテーション
このコードがサーブレットのためのアノテーションである。サーブレットのクラス宣言の直前に書くことによってURLパターン「/juice」がマッピングされる。
@WebServlet("/juice")
web.xml
web.xmlによる定義は、サーブレットクラスが一度サーブレット名に紐付けされ、さらにサーブレット名がURLパターンに紐付けされる。このURLパターンがサーブレットのURLパターンとなる。
URLパターン
<url-pattern> |
サーブレット名
<servlet-name> |
サーブレットクラス
<servlet-class> |
/juice | servlet1 | Servlet1 |
この関係を定義したweb.xmlを次に示す。
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <servlet> <servlet-name>servlet1</servlet-name> <servlet-class>Servlet1</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet1</servlet-name> <url-pattern>/juice</url-pattern> </servlet-mapping> </web-app> |
サンプルプログラム
このプログラムが、URLパターン「/juice」からマッピングされるサーブレットのクラスである。ここでは、アノテーション@WebServlet()を用いてURLパターンの宣言を行ったが、web.xmlによる宣言を行う時はアノテーションの宣言は不要である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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; @WebServlet("/juice")//[1] public class Servlet1 extends HttpServlet {//[2] protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//[3] PrintWriter out = response.getWriter();//[4] out.println("[5] Servlet1"); } } |
実行結果
ブラウザにURLパス:http://localhost:8080/Servlet/juice を入力し、Enterを押す。
表示されるブラウザ画面:
ブラウザにURLパス:http://localhost:8080/Servlet/JuiCeを入力し、Enterを押す。
表示されるブラウザ画面:サーブレットが選択できなかったことを示す画面が表示される。
サンプルプログラムの説明
それでは簡単にプログラムの解説をしてゆこう。
- [1] アノテーションによりURLパターン「/juice」を宣言する。
- [2] HttpServletを継承して、Servlet1クラスを定義する。
- [3] doGet()メソッドをオーバーライドして定義する。
- [4] HTMLを出力するためのoutオブジェクトを取得する。
- [5] outオブジェクトに「Servlet1」を出力する。
URLパターンのパスマッピング
パスマッピングとは、サーブレットに紐つけられたURLパターンとして、「/water/*」のような「/」で始まり「/*」の末尾で終わる文字列を使って一致するURLパターンを探すものだ。この「/」で始まり「/*」の末尾で終わる文字列をパス・プレフィックス(接頭辞)という。パスマッピングで「/*」は、「/*」以降の文字列がどんな文字列にも当てはまるものとされる。
Webコンテナは最も長いパス・プレフィックスと一致するパスを見つけようとする。例えば、URLパスのURLパターンが「/soda/grape/index.html」ならば、サーブレットのURLパターンはパス・プレフィックス「/soda/*」と「/soda/grape/*」の両方に一致する。この時、Webコンテナは「/」文字をセパレータとして、パスツリーを先頭のディレクトリから比較し、次のディレクトリというようにひとつずつ降りながら比較する。こうしてサーブレットのURLパターンの中で最も長い一致を選択する。それで、この場合は2番目のディレクトリも一致する「/soda/grape/*」が選択されることになる。
例を見てみよう。
URLパス:http://localhost:8080/Servlet/soda/grape/index.html
このURLパターン「/soda/grape/index.html」から選択されるのは、先に説明したように「/soda/*」より長い一致を持つパス・プレフィックス「/soda/grape/*」である。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/soda/grape/index.html | /soda/grape/* | ○ | ○ |
/soda/* | ○ | × | |
/water/* | × | × |
URLパス:http://localhost:8080/Servlet/soda/grape/index.bop
このURLパターン「/soda/grape/index.bop」からは、前と同じようにより長い「/soda/grape/*」が選択される。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/soda/grape/index. bop | /soda/grape/* | ○ | ○ |
/soda/* | ○ | × | |
/water/* | × | × |
URLパス:http://localhost:8080/Servlet/soda/index.html
このURLパターン「soda/index.html」は「/soda/*」には一致するものの「/soda/grape/*」には一致しないので、このURLパターンから選択されるのは、「/soda/*」となる。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/soda/grape/index.html | /soda/grape/* | × | × |
/soda/* | ○ | ○ | |
/water/* | × | × |
URLパス:http://localhost:8080/Servlet/water/*
このURLパターン「/water/*」は「/water/*」に一致し、他のパターンには一致しないので、このURLパターンから選択されるのは、「/water/*」となる。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/soda/grape/index.html | /soda/grape/* | × | × |
/soda/* | × | × | |
/water/* | ○ | ○ |
URLパス:http://localhost:8080/Servlet/water/index.html
このURLパターン「/water/index.html」は「/water/*」に一致し、このURLパターンから選択されるのは、「/water/*」となる。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/soda/grape/index.html | /soda/grape/* | × | × |
/soda/* | × | × | |
/water/* | ○ | ○ |
アノテーション
このコードがサーブレットのためのアノテーションである。サーブレットのクラス宣言の直前に書くことによってURLパターンがサーブレットへ次のようにマッピングされる。
- /soda/grape/* → Servlet2
- /water/* → Servlet3
- / soda /* → Servlet4
実際のコードの宣言部分は次のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 |
@WebServlet("/soda/grape/*") public class Servlet2 extends HttpServlet { ….. } @WebServlet("/soda/*") public class Servlet3 extends HttpServlet { ….. } @WebServlet("/water/*") public class Servlet4 extends HttpServlet { ….. } |
web.xml
web.xmlによる定義は、サーブレットクラスが一度サーブレット名に紐付けされ、さらにサーブレット名がURLパターンに紐付けされている。このURLパターンがサーブレットのURLパターンとなる。
URLパターン
<url-pattern> |
サーブレット名
<servlet-name> |
サーブレットクラス
<servlet-class> |
/soda/grape/* | servlet2 | Servlet2 |
/soda/* | servlet3 | Servlet3 |
/water/* | servlet4 | Servlet4 |
この関係を定義したweb.xmlを次に示す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <servlet> <servlet-name>servlet4</servlet-name> <servlet-class>Servlet4</servlet-class> </servlet> <servlet> <servlet-name>servlet3</servlet-name> <servlet-class>Servlet3</servlet-class> </servlet> <servlet> <servlet-name>servlet2</servlet-name> <servlet-class>Servlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet4</servlet-name> <url-pattern>/water/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>servlet3</servlet-name> <url-pattern>/soda/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>servlet2</servlet-name> <url-pattern>/soda/grape/*</url-pattern> </servlet-mapping> </web-app> |
サンプルプログラム
このプログラムが、URLパターン「/juice」からマッピングされるサーブレットのクラスである。ここでは、アノテーション@WebServlet()を用いてURLパターンの宣言を行ったが、web.xmlによる宣言を行う時はアノテーションの宣言は不要である。
1 2 3 4 5 6 7 8 9 |
(import は全てServlet1と同じ) @WebServlet("/soda/grape/*")//[1] public class Servlet2 extends HttpServlet {//[2] protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//[3] PrintWriter out = response.getWriter();//[4] out.println("[5] Servlet2"); } } |
1 2 3 4 5 6 7 8 9 |
(import は全てServlet1と同じ) @WebServlet("/soda/*")//[1] public class Servlet3 extends HttpServlet {//[2] protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//[3] PrintWriter out = response.getWriter();//[4] out.println("[5] Servlet3"); } } |
1 2 3 4 5 6 7 8 9 |
(import は全てServlet1と同じ) @WebServlet("/water/*")//[1] public class Servlet4 extends HttpServlet {//[2] protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//[3] PrintWriter out = response.getWriter();//[4] out.println("[5] Servlet4"); } } |
実行結果
ブラウザにURLパス:http://localhost:8080/Servlet/soda/grape/index.htmlを入力し、Enterを押す。
表示されるブラウザ画面:
ブラウザにURLパス:http://localhost:8080/Servlet/soda/grape/index.bopを入力し、Enterを押す。
表示されるブラウザ画面:
ブラウザにURLパス:http://localhost:8080/Servlet/soda/index.htmlを入力し、Enterを押す。
表示されるブラウザ画面:
ブラウザにURLパス:http://localhost:8080/Servlet/grape/ water/*を入力し、Enterを押す。
表示されるブラウザ画面:
ブラウザにURLパス:http://localhost:8080/Servlet/water/index.htmlを入力し、Enterを押す。
表示されるブラウザ画面:
サンプルプログラムの説明
どのサーブレットも、URLパターンと表示するサーブレットの番号が異なるだけで、それ以外は「URLパターンの正確な一致」のセクションのServlet1と同じコードになっている。
URLパターンの拡張子マッピング
URLパス最後の部分が拡張子(例:.jspなど)を含んでいたならば、コンテナは、その拡張子のためのリクエストを処理するサーブレットを見つけようとする。拡張子は、「.」文字の後の部分として定義されている。
サーブレットに紐付けされたURLパターンが「*.」で始まるならば、拡張子マッピングのために使われる。
URLパス:http://localhost:8080/Servlet/juice/orange.jsp
URLパス:http://localhost:8080/Servlet/index.jsp
これらのURLパターン「/juice/orange.jsp」と「/index.jsp」は、拡張子が「jsp」なので、このURLパターンから選択されるのは、「*.jsp」となる。
URLパスのURLパターン | サーブレットのURLパターン | 一致 | 選択 |
/juice/orange.jsp | *.jsp | ○ | ○ |
*.xxx | × | × | |
/index.jsp | *.jsp | ○ | ○ |
*.xxx | × | × |
アノテーション
このコードがサーブレットのためのアノテーションである。サーブレットのクラス宣言の直前に書くことによってURLパターン「*.jsp」がマッピングされる。
@WebServlet("*.jsp")
web.xml
web.xmlによる定義は、サーブレットクラスが一度サーブレット名に紐付けされ、さらにサーブレット名がURLパターンに紐付けされている。このURLパターンがサーブレットのURLパターンとなる。
URLパターン
<url-pattern> |
サーブレット名
<servlet-name> |
サーブレットクラス
<servlet-class> |
*.jsp | servlet5 | Servlet5 |
この関係を定義したweb.xmlを次に示す。
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <servlet> <servlet-name>servlet5</servlet-name> <servlet-class>Servlet5</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet5</servlet-name> <url-pattern>*.jsp</url-pattern> </servlet-mapping> </web-app> |
サンプルプログラム
このプログラムが、URLパターン「*.jsp」からマッピングされるサーブレットのクラスである。ここでは、アノテーション@WebServlet()を用いてURLパターンの宣言を行ったが、web.xmlによる宣言を行う時はアノテーションの宣言は不要である。
1 2 3 4 5 6 7 8 9 |
(import は全てServlet1と同じ) @WebServlet("*.jsp")//[1] public class Servlet5 extends HttpServlet {//[2] protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//[3] PrintWriter out = response.getWriter();//[4] out.println("[5] Servlet5"); } } |
実行結果
ブラウザにURLパス:http://localhost:8080/Servlet/*.jspを入力し、Enterを押す。
表示されるブラウザ画面:
ブラウザにURLパス:http://localhost:8080/Servlet/index.jspを入力し、Enterを押す。
表示されるブラウザ画面:
サンプルプログラムの説明
どのサーブレットも、URLパターンと表示するサーブレットの番号が異なるだけで、それ以外は「URLパターンの正確な一致」のセクションのServlet1と同じコードになっている。
どのURLパターンにも一致しない
もしサーブレットの選択において前の3つの規則(正確な一致、パスマッピング、拡張子マッピング)のどれにも当てはまらなかったなら、Webコンテナは要求されたものに替わる適切なコンテンツを使おうとする。もし、アプリケーションのためにデフォルトサーブレットが定義されていたならば、それが使われる。多くのWebコンテナが供給されているコンテンツのためにデフォルトサーブレットを提供している。
サーブレットに紐付けされたURLパターンが文字「/」だけならば、デフォルトサーブレットを定義していることになる。このため、どのURLパターンにも一致しない場合は、このデフォルトサーブレットが選択される。
デフォルトサーブレットは、「Webアプリケーションの設定(アノテーション)」か「Webアプリケーションの設定(web.xmlの設定)」の記事で説明しているので参考にできる。
まとめ
この記事では入力されたURLによってマッピングされたそれぞれ別のサーブレットによる出力が出てくる事を確認した。実際にサンプルプログラムを動作させながら仕組みについて理解を深めよう。