この記事ではサーブレットのライフサイクルについて説明する。
目次
ライフサイクルとは何か
サーブレット(Servlet)のライフサイクル(Servlet Life Cycle)とは、Webコンテナの中でサーブレットが生成されてから消去されるまでの過程である。サーブレットはJavaのクラスがそうであるようにインスタンスが生成され、そのインスタンスが要求されたサービスを実行する。そして、必要なくなったときに削除される。この過程がサーブレットのライフサイクルである。
この図は、サーブレットのライフサイクルを表したものである。
その詳細をはじまりから見てみよう。
はじまり
Javaで書かれたコードがコンパイルされてクラスファイルになる。そして、Webコンテナに配置されたところからサーブレットのライフサイクルが始まる。Webコンテナに配置されたサーブレットは、Webコンテナによってコントロールされる。そして、リクエストがサーブレットに割り当てられた時、Webコンテナは次のステップを実行する。
ステップ1:インスタンスの生成と初期化
もし、サーブレットのインスタンスが存在しなかったなら、Webコンテナは
- Servletクラスをロードする。
- Servletクラスのインスタンスを生成する。
- サーブレットのinit()メソッドを呼び出すことにより、サーブレットのインスタンスを初期化する。
ステップ2:リクエストによるサービスメソッド呼び出し
Webコンテナは、リクエストとレスポンス・オブジェクトを引数として渡すservive()メソッドを起動する。
インスタンスの削除:destroy()メソッド
もしインスタンスを削除することが必要であれば、Webコンテナはdestroy()メソッドを呼び出すことによりサーブレットを終了させる。
ライフサイクルイベント
サーブレットにはライフサイクルイベントを活用する仕組みがあり、サーブレットの状態を知ることができる。ライフサイクルイベントが発生した時、リスナーオブジェクトに呼び出されるメソッドを定義することによって、イベントをモニターしたり、対応した処理をしたりすることができる。
ライフサイクルイベントして活用できる主なものを以下に示す。コンテキストとは、実行情報のことである。
オブジェクト |
イベントの詳細 |
Webコンテキスト |
イベント:初期化と消滅。 Webアプリケーションのサーブレットコンテキスト(実行)の変化について通知するためのイベントである。 リスナークラス:javax.servlet.ServletContextListener |
セッション |
イベント:生成、無効、有効、パッシベーション、タイムアウト Webアプリケーション内のセッションの変化について通知するためのイベントである。 リスナークラス: イベントクラス: |
リクエスト |
イベント:サーブレトリクエストがWebコンポーネントによって処理されることが始まった。
リスナークラス:javax.servlet.ServletRequestListener イベントクラス:javax.servlet. ServletRequestEvent |
どのように活用できるのかを、サンプルプログラムで見てみよう。
ServletContextListenerの仕様
ServletContextListenerクラスにある二つのメソッドのAPI仕様を示す。
contextInitialized(ServletContextEvent sce)
戻り値の型 |
void |
内容 |
ウェブアプリケーションの初期化のプロセスが始まっている通知である。すべてのサーブレットコンテキストのリスナーは、Webアプリケーションのどんなフィルターもしくはサーブレットでも、初期化される前にコンテキストの初期化を知らされている。 引数: |
contextDestroyed(ServletContextEvent sce)
戻り値の型 |
void |
内容 |
サーブレットコンテキストがシャットダウンされようとしている通知である。サーブレットコンテキストリスナーがコンテキストの破壊について知らされる前に、すべてのサーブレットとフィルターは破壊されてしまっている。 引数:sce - 破壊されたサーブレットコンテキストについての情報 |
HttpSessionListenerの仕様
HttpSessionListenerクラスにある二つのメソッドのAPI仕様を示す。
sessionCreated(HttpSessionEvent se)
戻り値の型 |
void |
内容 |
セッションが生成されたことを通知。 引数:se – 通知イベント。 |
sessionDestroyed(HttpSessionEvent se)
戻り値の型 |
void |
内容 |
セッションが無効になることを通知。 引数:se – 通知イベント。 |
ServletRequestListenerの仕様
ServletRequestListenerクラスにある二つのメソッドのAPI仕様を示す。
sessionCreated(HttpSessionEvent se)
戻り値の型 |
void |
内容 |
Webアプリケーションのスコープから入ることを通知。 引数:sre – リクエストについての情報。 |
requestDestroyed(ServletRequestEvent sre)
戻り値の型 |
void |
内容 |
Webアプリケーションのスコープから出ることを通知。 引数:sre – リクエストについての情報。 |
コンテキストイベントのサンプルプログラム
ライフサイクルイベントを受け取るためには、ListenerクラスにListenerインターフェースを実装することによって定義できる。このサンプルプログラムでは、コンテキストイベントをListenerクラスで受け取っている。その結果はログとして出力される。
このサンプルプログラムを実行させるために、Webサーバーを起動し、しばらく待ってWebサーバーを終了している。この手順で、下記の実行結果が得られる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener()//[1] public class ContextEventListener implements ServletContextListener {//[2] public void contextInitialized(ServletContextEvent event) {//[3] ServletContext context = event.getServletContext();//[4] context.log("[5]コンテキスト:開始"); } public void contextDestroyed(ServletContextEvent event) {//[6] ServletContext context = event.getServletContext();//[7] context.log("コンテキスト:終了");//[8] } } |
実行結果
1 2 3 4 |
1 07, 2017 11:41:21 午後 org.apache.catalina.core.ApplicationContext log 情報: [5] コンテキスト:開始 1 07, 2017 11:41:29 午後 org.apache.catalina.core.ApplicationContext log 情報: [8] コンテキスト:終了 |
サンプルプログラムの説明
それでは簡単にプログラムの解説をしてゆこう。
- [1] アノテーションの宣言をする。
- [2] ServletContextListenerを継承して、ContextEventListenerクラスを提起する。
- [3] 修飾子publicでメソッドcontextInitialized ()を宣言する。
- [4] eventオブジェクトからコンテキストを取得する。
- [5] 「コンテキスト:開始」をコンテキストのログに出力する。
- [6] 修飾子publicでメソッドcontextDestroyed ()を宣言する。
- [7] eventオブジェクトからコンテキストを取得する。
- [8] 「コンテキスト:終了」をコンテキストのログに出力する。
セッションイベントのサンプルプログラム
ライフサイクルイベントを受け取るためには、ListenerクラスにListenerインターフェースを実装することによって定義できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import javax.servlet.ServletContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent ; import javax.servlet.http.HttpSessionListener; @WebListener()//[1] public class SessionEventListener implements HttpSessionListener {//[2] public void sessionCreated(HttpSessionEvent event) {//[3] HttpSession session = event.getSession();//[4] ServletContext context = session.getServletContext();//[5] context.log("[6] セッション開始: ID = " + session.getId()); } public void sessionDestroyed(HttpSessionEvent event) {//[7] HttpSession session = event.getSession();//[8] ServletContext context = session.getServletContext();//[9] context.log("[10] セッション終了:ID = " + session.getId()); } } |
実行結果
1 2 3 4 |
1 09, 2017 9:55:57 午前 org.apache.catalina.core.ApplicationContext log 情報: [6] セッション開始: ID = DF2FA0C602123211CF6FE75860B5836E 1 09, 2017 9:55:58 午前 org.apache.catalina.core.ApplicationContext log 情報: [10] セッション終了:ID = DF2FA0C602123211CF6FE75860B5836E |
サンプルプログラムの説明
それでは簡単にプログラムの解説をしてゆこう。
- [1] アノテーションの宣言をする。
- [2] HttpSessionListenerを継承して、SessionEventListenerクラスを定義する。
- [3] 修飾子publicでメソッドsessionCreated ()を宣言する。
- [4] eventオブジェクトからセッションを取得する。
- [5] sessionオブジェクトからコンテキストを取得する。
- [6] 「セッション開始: ID =(セッションIDを取得)」をコンテキストのログに出力する。
- [7] 修飾子publicでメソッドsessionDestroyed ()を宣言する。
- [8] eventオブジェクトからリクエストを取得する。
- [9] eventオブジェクトからコンテキストを取得する。
- [10] 「セッション終了: ID =(セッションIDを取得)」をコンテキストのログに出力する。
リクエストイベントのサンプルプログラム
ライフサイクルイベントを受け取るためには、ListenerクラスにListenerインターフェースを実装することによって定義できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener; @WebListener()//[1] public class RequestEventListener implements ServletRequestListener {//[2] public void requestInitialized(ServletRequestEvent event) {//[3] ServletRequest request = event.getServletRequest();//[4] ServletContext context = event.getServletContext();//[5] context.log("[6] " + request.getRemoteAddr() + "のリクエストがスコープに入る"); } public void requestDestroyed(ServletRequestEvent event) {//[7] ServletRequest request = event.getServletRequest();//[8] ServletContext context = event.getServletContext();//[9] context.log("[10] " + request.getRemoteAddr() + "のリクエストがスコープから出る"); } } |
実行結果
1 2 3 4 |
1 08, 2017 1:26:16 午後 org.apache.catalina.core.ApplicationContext log 情報: [6] 0:0:0:0:0:0:0:1のリクエストがスコープに入る 1 08, 2017 1:26:16 午後 org.apache.catalina.core.ApplicationContext log 情報: [10] 0:0:0:0:0:0:0:1のリクエストがスコープから出る |
サンプルプログラムの説明
それでは簡単にプログラムの解説をしてゆこう。
- [1] アノテーションの宣言をする。
- [2] ServletRequestListenerを継承して、RequestEventListenerクラスを定義する。
- [3] 修飾子publicでメソッドrequestInitialized ()を宣言する。
- [4] eventオブジェクトからリクエストを取得する。
- [5] eventオブジェクトからコンテキストを取得する。
- [6] 「(リクエストを出したクライアントのアドレス)のリクエストがスコープに入る」をコンテキストのログに出力する。
- [7] 修飾子publicでメソッドrequestDestroyed ()を宣言する。
- [8] eventオブジェクトからリクエストを取得する。
- [9] eventオブジェクトからコンテキストを取得する。
- [10] 「(リクエストを出したクライアントのアドレス)のリクエストがスコープから出る」をコンテキストのログに出力する。
イベント発生させたサーブレットプログラム
ライフサイクルイベントを受け取るためには、ListenerクラスにListenerインターフェースを実装することによって定義できる。
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 28 29 30 31 32 33 |
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; import javax.servlet.http.HttpSession; @WebServlet("/EventTest")//[1] public class ServleEventTest extends HttpServlet {//[2] public void init() throws ServletException{//[3] log("[4] init()"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//[5] PrintWriter out = response.getWriter();//[6] out.write("[7] Event Test Servlet => "); HttpSession session = request.getSession(false);//[8] if (session == null){//[9] session = request.getSession(true);//[10] out.write("[11] Session has started"); return;//[12] } out.write("[13] Session has closed"); session.invalidate();//[14] } public void destroy(){//[15] log("[16] destroy()"); } } |
実行結果
手順1)Webサーバーを起動する。
ログ:
1 2 3 4 5 6 |
1 09, 2017 11:13:04 午前 org.apache.catalina.core.StandardService startInternal 情報: サービス Catalina を起動します 1 09, 2017 11:13:04 午前 org.apache.catalina.core.StandardEngine startInternal 情報: Starting Servlet Engine: Apache Tomcat/7.0.73 1 09, 2017 11:13:05 午前 org.apache.catalina.core.ApplicationContext log 情報: [5] コンテキスト:開始 |
手順2)ブラウザにURL: http://localhost:8080/Servlet/EventTest と入力し、Enterを押す。
ブラウザ画面:
ログ:
1 2 3 4 5 6 |
1 09, 2017 11:13:14 午前 org.apache.catalina.core.ApplicationContext log 情報: [6] 0:0:0:0:0:0:0:1のリクエストがスコープに入る 1 09, 2017 11:13:14 午前 org.apache.catalina.core.ApplicationContext log 情報: lifecycleevent.ServleEventTest: [4] init() 1 09, 2017 11:13:14 午前 org.apache.catalina.core.ApplicationContext log 情報: [6] セッション開始: ID = A30AA242438DEE29A6B075B366BEC09B |
手順3)ブラウザの更新ボタンを押す。
ブラウザ画面:
ログ:
1 2 3 4 5 6 |
1 09, 2017 11:15:08 午前 org.apache.catalina.core.ApplicationContext log 情報: [6] 0:0:0:0:0:0:0:1のリクエストがスコープに入る 1 09, 2017 11:15:08 午前 org.apache.catalina.core.ApplicationContext log 情報: [10] セッション終了:ID = A30AA242438DEE29A6B075B366BEC09B 1 09, 2017 11:15:08 午前 org.apache.catalina.core.ApplicationContext log 情報: [10] 0:0:0:0:0:0:0:1のリクエストがスコープから出る |
手順4)Webサーバーを停止する。
ログ:
1 2 3 4 5 6 |
1 09, 2017 11:16:01 午前 org.apache.catalina.core.StandardService stopInternal 情報: サービス Catalina を停止します 1 09, 2017 11:16:01 午前 org.apache.catalina.core.ApplicationContext log 情報: lifecycleevent.ServleEventTest: [16] destroy() 1 09, 2017 11:16:01 午前 org.apache.catalina.core.ApplicationContext log 情報: [8] コンテキスト:終了 |
サンプルプログラムの説明
それでは簡単にプログラムの解説をしてゆこう。
- [1] アノテーションの宣言をする。
- [2] HttpServletを継承して、ServleEventTestクラスを定義する。
- [3] 修飾子publicでメソッドServletException ()を宣言する。
- [4] 「init()」をコンテキストのログに出力する。
- [5] 修飾子publicでメソッドdoGet ()をオーバーライドする。
- [6] HTMLを出力するためのoutオブジェクトを取得する。
- [7] outオブジェクトに「Event Test Servlet => 」を出力する。
- [8] セッションsessionオブジェクトを取得する。
- [9][10] セッションが開始されていない時、セッションを開始する。
- [11][12] outオブジェクトに「Session has started」を出力し、処理を終了する。
- [13] outオブジェクトに「Session has closed」を出力する。
- [14] セッションを終了する。
ライフサイクルの詳細
サーブレットの生成と初期化
@WebServletアノテーションを使ってWebアプリケーションのサーブレット・コンポーネントの定義を行う。このアノテーションはクラスの仕様に含められ、宣言されたサーブレットについての属性情報を含んでいる。アノテーションが付いているサーブレットは少なくともひとつのURLパターンを属性情報として含んでいなければならない。これは、アノテーションのurlPatternsまたはvalue 属性によって特定される。他の全ての属性はオプションで、指定されなければデフォルト設定となる。アノテーションの属性がURLパターンの時だけは、value属性を使用してください。他方、他の属性が使われている時は、urlPatternsを使用してください。
例えば、次のコードの抜粋は、URLパターン「/report」を持つサーブレットを定義する。@WebServletでアノテーションを付加されたクラスはjavax.servlet.http.HttpServletを継承していなければならない。
@WebServlet("/report")public class MoodServlet extends HttpServlet { ...
Webコンテナは、サーブレットクラスがローディグされ初期化された後、クライアントから送られたリクエストが来る前に初期化しなければならない。永続的な構成データを読むこと、リソースを初期化すること、その他の一度だけのアクティビティを実行することをサーブレットに許可してこのプロセスをカスタマイズするために、サーブレットのinit()メソッドをオーバーライドするか、@WebServletアノテーションのinitParams属性を指定することができる。initParams属性は、@WebInitParamアノテーションを含んでいる。もし初期化プロセスを完了できない時は、サーブレットはUnavailableException例外を発生させる。特定のサーブレットによって必要とされるデータを提供するために初期化パラメータを使用してください。対照的に、コンテキスト属性はWebアプリケーションのすべてのコンポーネントに有効なデータを提供する。
サービスメソッド
サーブレットによって提供されるサービスは、GenericServletのserviceメソッドの中、httpServletオブジェクトのdoMethods(Methodの部分は、Get, Delete, Options, Post, Put, or Trace置き換えられる)の中、またはサーブレットインターフェースを実装したクラスによって定義された他のプロトコルに依存したメソッドの中に実装したものである。serviceメソッドという言葉は、クライアントにサービスを提供するサーブレットクラスの中にあるどんなメソッドのために使われる。
サービス(serviceメソッドのための一般的なパターンは、リクエストから情報を抽出すること、そしてその情報に基づいて外部リソースにアクセスすること、そしてレスポンスに反映させる。HTTPサーブレットのために、レスポンスに反映させるための正しい手順は、次の通りです。
- レスポンスから出力ストリームを取り出す。
- レスポンスヘッダーに書き込む。
- 出力ストリームにすべてのコンテンツ本体を書き込む。
レスポンスがコミットされる前に、レスポンスヘッダーは設定されなければならない。レスポンスがコミットされた後に設定または追加されたどんな試みも、Webコンテナは無視する。
まとめ
この記事では、サーブレットのライフサイクルについて紹介した。サンプルプログラムを実際に動作させるなどして、仕組みを理解しよう。