
サーブレット&JSPの基本|initとdestroyの基本
サーブレットは1回作られて何度も使われる。initとdestroyで、開始時の準備と終了時の後片付けを理解しよう
これまでサーブレットでは、主にdoGetメソッドやdoPostメソッドを使ってきました。
ブラウザからGETリクエストが送られたときはdoGetメソッド、POSTリクエストが送られたときはdoPostメソッドが実行されます。この2つは、ServletとJSPの学習ではとてもよく登場する基本メソッドです。
しかし、サーブレットクラスには、doGetやdoPost以外にも重要なメソッドがあります。
その代表が、initメソッドとdestroyメソッドです。
initメソッドは、サーブレットのインスタンスが作成された直後に実行されるメソッドです。最初に一度だけ行いたい準備処理を書くときに使います。
destroyメソッドは、サーブレットのインスタンスが破棄される直前に実行されるメソッドです。最後に一度だけ行いたい後片付けを書くときに使います。
この記事では、まずサーブレットクラスがどのようにインスタンス化され、どのタイミングで再利用されるのかを確認します。そのうえで、initメソッドとdestroyメソッドの役割、動作確認の方法、使うときの注意点を学んでいきます。
サーブレットクラスはそのままでは実行できない
これまで、サーブレットクラスを実行する、という表現を使ってきました。
ただし、Javaのしくみとしては、クラスそのものをそのまま実行しているわけではありません。
doGetやdoPostはstaticが付いていないインスタンスメソッドです。そのため、実行するには、そのクラスからインスタンスを作成する必要があります。
通常のJavaクラスであれば、開発者がnewを使ってインスタンスを作成します。
たとえば、普通のクラスなら次のようなイメージです。
| 種類 | インスタンス化する人 |
|---|---|
| 通常のJavaクラス | 開発者がnewを使って作成する |
| サーブレットクラス | サーブレットコンテナが自動で作成する |
サーブレットクラスの場合、開発者が自分でnewしてインスタンスを作成する必要はありません。
ブラウザからリクエストが届いたとき、アプリケーションサーバ内のサーブレットコンテナが、必要に応じてサーブレットクラスをインスタンス化してくれます。
サーブレットコンテナがインスタンス化してくれる
サーブレットクラスは、Tomcatのようなアプリケーションサーバ上で動作します。
その中で、サーブレットの生成や実行、破棄などを管理している仕組みがサーブレットコンテナです。
開発者は、サーブレットクラスを作成し、URLパターンを指定します。
その後、ブラウザからそのURLへリクエストが送られると、サーブレットコンテナが対象のサーブレットクラスを探し、インスタンス化し、doGetやdoPostを呼び出します。
サーブレット実行までの大まかな流れ
| 順番 | 内容 |
|---|---|
| 1 | ブラウザからサーブレットのURLへリクエストを送る |
| 2 | サーブレットコンテナが対象のサーブレットクラスを確認する |
| 3 | 必要に応じてサーブレットクラスをインスタンス化する |
| 4 | リクエスト方法に応じてdoGetまたはdoPostを呼び出す |
| 5 | サーブレットがレスポンスを作成してブラウザへ返す |
このように、サーブレットのインスタンス化は、開発者が明示的に行うのではなく、サーブレットコンテナが自動で行います。

リクエストのたびにインスタンス化されるわけではない
ここで注意したいのは、サーブレットクラスがリクエストのたびに毎回インスタンス化されるわけではない、という点です。
アプリケーションサーバにとって、インスタンスの生成と破棄は負荷のかかる処理です。
もし、リクエストが届くたびにサーブレットのインスタンスを作成し、レスポンス後に毎回破棄していたら、アクセスが増えたときにサーバへの負荷が大きくなります。
そこで、サーブレットコンテナは、一度作成したサーブレットインスタンスをすぐには破棄しません。
初回リクエスト時にサーブレットインスタンスを作成し、その後のリクエストでも同じインスタンスを再利用します。
サーブレットインスタンスの基本
| タイミング | 動作 |
|---|---|
| 初回リクエスト時 | サーブレットクラスがインスタンス化される |
| 2回目以降のリクエスト時 | 作成済みのインスタンスが再利用される |
| Webアプリケーション終了時 | インスタンスが破棄される |
このしくみにより、サーバは効率よくリクエストを処理できます。
図1:サーブレットクラスのインスタンス化と再利用

この図から分かること
サーブレットクラスは、初回リクエスト時にサーブレットコンテナによってインスタンス化されます。
一度作成されたインスタンスは、リクエストが終わったあともすぐには破棄されません。2回目以降のリクエストでは、作成済みのインスタンスが再利用されます。
このしくみにより、リクエストのたびにインスタンスを作成する負荷を減らし、効率よく処理できます。
インスタンスは、Webアプリケーションが終了するときなどに破棄されます。
JSPファイルも内部ではインスタンス化される
JSPファイルも、実行されるときには内部でサーブレットとして扱われます。
JSPファイルに初めてリクエストが届くと、まずJSPファイルがサーブレットクラスに変換されます。そのあと、変換されたサーブレットクラスがコンパイルされ、インスタンス化されます。
そのため、JSPファイルへの初回アクセスは、少し時間がかかる場合があります。
JSP実行時の流れ
| 順番 | 内容 |
|---|---|
| 1 | JSPファイルに初回リクエストが届く |
| 2 | JSPファイルがサーブレットクラスに変換される |
| 3 | 変換されたサーブレットクラスがコンパイルされる |
| 4 | インスタンス化される |
| 5 | HTMLが生成され、ブラウザへ返される |
生成されたインスタンスは、Webアプリケーションが終了するまで、またはJSPファイルが更新されるまで再利用されます。
つまり、JSPもサーブレットと同じように、毎回ゼロから作り直されるわけではありません。

initメソッドとdestroyメソッドとは
サーブレットクラスは、HttpServletを継承して作成します。
HttpServletには、doGetやdoPostのほかに、initメソッドとdestroyメソッドも用意されています。
これらは、開発者が直接呼び出すメソッドではありません。
決まったタイミングで、サーブレットコンテナが自動的に呼び出します。
initとdestroyの役割
| メソッド | 呼び出されるタイミング | 主な役割 |
|---|---|---|
| init | サーブレットインスタンスが作成された直後 | 初期化処理を行う |
| destroy | サーブレットインスタンスが破棄される直前 | 後片付け処理を行う |
initメソッドは、最初に一度だけ行いたい準備処理に向いています。
destroyメソッドは、最後に一度だけ行いたい後片付けに向いています。
initメソッドが実行されるタイミング
initメソッドは、サーブレットクラスのインスタンスが作成された直後に実行されます。
多くの場合、対象のサーブレットに初めてリクエストが届いたタイミングで、次のように処理が進みます。
初回リクエスト時の流れ
| 順番 | 内容 |
|---|---|
| 1 | ブラウザから初回リクエストが届く |
| 2 | サーブレットコンテナがサーブレットをインスタンス化する |
| 3 | initメソッドが実行される |
| 4 | doGetまたはdoPostが実行される |
| 5 | レスポンスがブラウザへ返される |
initメソッドは、インスタンス作成後に一度だけ実行されます。
そのため、リクエストのたびに毎回実行されるわけではありません。
destroyメソッドが実行されるタイミング
destroyメソッドは、サーブレットインスタンスが破棄される直前に実行されます。
たとえば、サーバを停止したとき、Webアプリケーションが終了したとき、再読み込みによって古いインスタンスが破棄されるときなどです。
Webアプリケーション終了時の流れ
| 順番 | 内容 |
|---|---|
| 1 | Webアプリケーションが終了する |
| 2 | サーブレットコンテナがサーブレットインスタンスの破棄を準備する |
| 3 | destroyメソッドが実行される |
| 4 | サーブレットインスタンスが破棄される |
destroyメソッドも、通常のリクエストごとに実行されるわけではありません。
サーブレットインスタンスが終わる直前に、一度だけ実行されます。
図2:initメソッドとdestroyメソッドの実行タイミング

この図から分かること
initメソッドは、サーブレットインスタンスが作成された直後に実行されます。
そのあと、リクエストに応じてdoGetやdoPostが実行されます。
destroyメソッドは、サーブレットインスタンスが破棄される直前に実行されます。
initは最初に一度だけ、destroyは最後に一度だけ実行される、という流れを押さえておくと理解しやすくなります。
initメソッドをオーバーライドする
initメソッドは、HttpServletから継承しているメソッドです。
サブクラスでオーバーライドすると、サーブレットインスタンス作成直後に行いたい処理を書けます。
基本形は次のようになります。
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 最初の1回だけ実行したい処理を書く
}この形で大切なのは、super.init(config)を呼び出すことです。
super.init(config)は、スーパークラス側の初期化処理を実行するためのものです。自分でinitメソッドをオーバーライドする場合も、先にスーパークラスのinitを実行しておく必要があります。
initメソッドに書く処理の例
| 処理 | 内容 |
|---|---|
| 初期値の設定 | カウンタや設定値を初期状態にする |
| 共通データの準備 | アプリケーションスコープに初期データを保存する |
| 接続準備 | データベース接続などの準備を行う |
ただし、Webアプリケーション全体の初期化を特定のサーブレットのinitに任せると、リクエスト順による問題が起こることがあります。この注意点は、後半で確認します。
destroyメソッドをオーバーライドする
destroyメソッドも、HttpServletから継承しているメソッドです。
サブクラスでオーバーライドすると、サーブレットインスタンスが破棄される直前に行いたい処理を書けます。
基本形は次のようになります。
public void destroy() {
// 最後の1回だけ実行したい処理を書く
}destroyメソッドでは、終了時の後片付けを行います。
destroyメソッドに書く処理の例
| 処理 | 内容 |
|---|---|
| 終了ログの出力 | サーブレットが終了することをコンソールに表示する |
| 使用中リソースの解放 | 開いていた接続やファイルなどを閉じる |
| 一時データの整理 | 終了前に不要な情報を片付ける |
destroyメソッドは、リクエスト処理の途中で毎回実行されるものではありません。
サーブレットインスタンスが破棄される直前に実行されるため、最後に一度だけ行いたい処理を書く場所として考えます。
Eclipseでinitとdestroyを追加する方法
Eclipseでサーブレットクラスを作成するとき、継承したメソッドを選択して追加できます。
サーブレット作成画面で、initやdestroyにチェックを入れると、テンプレートとしてメソッドが作成されます。

Eclipseで追加できる主なメソッド
| メソッド | 用途 |
|---|---|
| doGet | GETリクエストを処理する |
| doPost | POSTリクエストを処理する |
| init | インスタンス作成直後の初期化処理を行う |
| destroy | インスタンス破棄直前の後片付けを行う |
もちろん、あとから自分でメソッドを追加してもかまいません。
Eclipseの作成画面を使うと、メソッド名や引数の書き間違いを減らせるので、慣れないうちは便利です。
initとdestroyの動作を確認するサーブレット
ここからは、実際にinitメソッドとdestroyメソッドの動作を確認できるサーブレットを作成します。
今回は、バッチ処理の進行状況確認ページをイメージしたサンプルにします。
このサーブレットでは、初回リクエスト時にinitメソッドで確認回数を0としてアプリケーションスコープへ保存します。
その後、doGetメソッドが実行されるたびに確認回数を1つ増やし、ブラウザに表示します。
destroyメソッドでは、サーブレットが破棄される直前に、コンソールへメッセージを出力します。
サンプルの動き
| タイミング | 実行される処理 |
|---|---|
| 初回リクエスト時 | インスタンス化されたあと、initメソッドで確認回数を初期化する |
| リクエストごと | doGetメソッドで確認回数を増やして画面に表示する |
| サーバ停止や再読み込み時 | destroyメソッドで終了メッセージをコンソールに表示する |
ブラウザには確認回数が表示されます。
Eclipseのコンソールには、initが実行されたことやdestroyが実行されたことを表すメッセージが表示されます。
initとdestroyを持つサーブレットクラスの例
ファイル名: BatchMonitorServlet.java
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/BatchMonitorServlet")
public class BatchMonitorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 確認回数を表すIntegerインスタンスを新規作成し、
// アプリケーションスコープに保存
Integer checkCount = 0;
ServletContext application = config.getServletContext();
application.setAttribute("batchCheckCount", checkCount);
System.out.println("BatchMonitorServletのinitが実行されました");
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// アプリケーションスコープに保存された確認回数を増加
ServletContext application = this.getServletContext();
Integer checkCount =
(Integer) application.getAttribute("batchCheckCount");
checkCount++;
application.setAttribute("batchCheckCount", checkCount);
// HTMLを出力
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\">");
out.println("<title>バッチ監視画面</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>バッチ監視画面</h1>");
out.println("<p>進行状況の確認回数:" + checkCount + "</p>");
out.println("<p>売上日次集計:実行中</p>");
out.println("<p>在庫更新:待機中</p>");
out.println("<a href=\"BatchMonitorServlet\">更新</a>");
out.println("</body>");
out.println("</html>");
}
public void destroy() {
System.out.println("BatchMonitorServletのdestroyが実行されました");
}
}このサーブレットは、servletパッケージに作成します。
プロジェクト名をstudyとしている場合、ブラウザから次のURLで実行できます。
ブラウザの表示例

BatchMonitorServlet.javaのポイント
BatchMonitorServlet.javaでは、init、doGet、destroyの3つのメソッドを使っています。
メソッドごとの役割
| メソッド | 役割 |
|---|---|
| init | 初回に確認回数を0としてアプリケーションスコープへ保存する |
| doGet | リクエストごとに確認回数を1増やして画面に表示する |
| destroy | サーブレット破棄直前にコンソールへメッセージを出力する |
initメソッドでは、Integer型のcheckCountを0で作成し、batchCheckCountという属性名でアプリケーションスコープへ保存しています。
doGetメソッドでは、アプリケーションスコープからbatchCheckCountを取得し、値を1増やしてから保存し直しています。
destroyメソッドでは、System.out.printlnを使って、destroyが実行されたことをEclipseのコンソールへ出力しています。
initで初期値を用意する
BatchMonitorServlet.javaでは、確認回数の初期値をinitメソッドで用意しています。
サーブレットインスタンスが作成された直後に次の処理が実行されます。
Integer checkCount = 0;
ServletContext application = config.getServletContext();
application.setAttribute("batchCheckCount", checkCount);この処理によって、doGetメソッドが実行される前に、アプリケーションスコープへbatchCheckCountが保存されます。
その後、doGetメソッドではこの値を取得して、確認回数を増やします。
initで初期化するメリット
| 内容 | 説明 |
|---|---|
| 初回だけ実行できる | リクエストのたびに初期化されない |
| doGetの前に準備できる | doGetで使うデータを先に用意できる |
| 初期化処理を分けられる | リクエスト処理と準備処理を分離できる |
初期化処理をdoGetの中に書くと、毎回初期化されてしまう可能性があります。
initに書くことで、サーブレットインスタンス生成直後の一度だけ実行する処理として整理できます。
System.out.printlnの表示先
BatchMonitorServlet.javaでは、initとdestroyの動作を確認するために、System.out.printlnを使っています。
この出力は、ブラウザには表示されません。
表示されるのは、Eclipseのコンソールビューです。

表示される場所
| 出力方法 | 表示先 |
|---|---|
| out.println | ブラウザ |
| System.out.println | Eclipseのコンソールビュー |
ブラウザに表示したい内容は、PrintWriterのout.printlnでHTMLとして出力します。
サーバ側で処理の流れを確認したい内容は、System.out.printlnでコンソールに出力します。
初回リクエスト時には、コンソールにBatchMonitorServletのinitが実行されましたと表示されます。
サーバの停止やWebアプリケーションの再読み込みなどでサーブレットインスタンスが破棄されると、BatchMonitorServletのdestroyが実行されましたと表示されます。
図3:ブラウザ表示とコンソール出力の違い

この図から分かること
out.printlnで出力したHTMLは、ブラウザに表示されます。
一方、System.out.printlnで出力したメッセージは、ブラウザには表示されず、Eclipseのコンソールビューに表示されます。
initやdestroyの実行タイミングを確認したい場合は、コンソールビューを見る必要があります。
初回リクエストではinitのメッセージが表示され、サーバ停止やWebアプリケーション終了時にはdestroyのメッセージが表示されます。
動作確認の手順
BatchMonitorServlet.javaを作成したら、動作を確認してみましょう。
ブラウザから確認する方法
| 手順 | 内容 |
|---|---|
| 1 | 動的WebプロジェクトstudyをTomcat11_Java25に追加する |
| 2 | サーバを起動する |
| 3 | ブラウザでhttp://localhost:8080/study/BatchMonitorServletへアクセスする |
| 4 | ブラウザに進行状況の確認回数が表示されることを確認する |
| 5 | 更新リンクをクリックする |
| 6 | 確認回数が1ずつ増えることを確認する |
| 7 | Eclipseのコンソールビューにinitのメッセージが表示されることを確認する |
| 8 | サーバを停止する |
| 9 | Eclipseのコンソールビューにdestroyのメッセージが表示されることを確認する |
Eclipseから実行する場合は、BatchMonitorServlet.javaを選択してサーバーで実行しても確認できます。
ただし、initが実行されるのは、サーブレットインスタンスが作成されたときです。すでにインスタンスが作成済みの場合、画面を更新してもinitのメッセージは毎回表示されません。
更新リンクを押すたびに増える理由
BatchMonitorServlet.javaでは、更新リンクをクリックするたびに、同じサーブレットへGETリクエストを送っています。
<a href="BatchMonitorServlet">更新</a>このリンクをクリックすると、doGetメソッドが再び実行されます。
doGetでは、アプリケーションスコープからbatchCheckCountを取得し、1増やしてから保存し直します。
更新時の流れ
| 順番 | 内容 |
|---|---|
| 1 | 更新リンクをクリックする |
| 2 | BatchMonitorServletへGETリクエストが送られる |
| 3 | doGetメソッドが実行される |
| 4 | batchCheckCountを取得する |
| 5 | 値を1増やす |
| 6 | アプリケーションスコープに保存し直す |
| 7 | 画面に確認回数を表示する |
initは最初の一度だけです。
更新リンクをクリックするたびに実行されるのは、doGetメソッドです。
initを使うときの注意点
initメソッドは、サーブレットインスタンスが作成された直後に実行される便利なメソッドです。
ただし、使い方には注意が必要です。
特に、Webアプリケーション全体で使うデータを、特定のサーブレットのinitで初期化する場合は気をつける必要があります。
なぜなら、initはそのサーブレットがインスタンス化されたときに実行されるからです。
通常は、対象のサーブレットに初めてリクエストが届いたときにインスタンス化されます。
つまり、そのサーブレットがまだリクエストされていなければ、initも実行されていません。
リクエスト順によって初期化が間に合わないことがある
たとえば、アプリケーション全体で使う設定情報を、ServletAのinitで作成してアプリケーションスコープに保存していたとします。
しかし、最初にリクエストされたのがServletBだった場合、ServletAのinitはまだ実行されていません。
そのため、ServletBがアプリケーションスコープから設定情報を取得しようとしても、nullになる可能性があります。
リクエスト順による問題
| 状況 | 結果 |
|---|---|
| ServletAが先にリクエストされる | ServletAのinitで設定情報が保存される |
| その後ServletBがリクエストされる | ServletBは設定情報を取得できる |
| ServletBが先にリクエストされる | ServletAのinitがまだ実行されていない |
| ServletBが設定情報を取得する | nullになる可能性がある |
このように、initの実行タイミングは、リクエストされた順番に影響されます。
Webアプリケーション全体の初期化を確実に行いたい場合は、特定のサーブレットのinitだけに頼らない設計が必要です。
Webアプリケーション全体の初期化には別の仕組みを考える
initメソッドは、そのサーブレット自身の準備には向いています。
たとえば、そのサーブレットだけで使う初期値を用意する、といった使い方です。
一方、Webアプリケーション全体で必ず必要になる初期化処理を、特定のサーブレットのinitに書くと、リクエスト順によって実行されない可能性があります。
initが向いている処理と注意が必要な処理
| 処理 | initに向いているか |
|---|---|
| そのサーブレットだけで使う初期値を用意する | 向いている |
| そのサーブレットの動作確認用カウンタを初期化する | 向いている |
| Webアプリケーション全体で必ず必要な共有データを初期化する | 注意が必要 |
| ほかのサーブレットも必ず使う共通設定を初期化する | 注意が必要 |
Webアプリケーション全体に関わる初期化には、リクエスト順に左右されにくい仕組みを使うのが安全です。
このような仕組みとして、別の場面でリスナーを使う方法を学習します。
サーブレットクラスのフィールドは使えるのか
サーブレットクラスでも、通常のJavaクラスと同じようにフィールドを定義できます。
たとえば、訪問回数や確認回数を保存するためのフィールドを作ることもできます。
ただし、サーブレットクラスのフィールドは慎重に扱う必要があります。
なぜなら、サーブレットインスタンスは複数のリクエストで再利用されるからです。
つまり、サーブレット内のフィールドも、複数のリクエストで共有されることになります。
フィールドを使ったサーブレットクラスの例
次の例では、確認回数をアプリケーションスコープではなく、サーブレットクラスのフィールドとして持たせています。
ただし、このような使い方は注意が必要です。まずは動きを知るための参考例として確認しましょう。
ファイル名: FieldBatchCounterServlet.java
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/FieldBatchCounterServlet")
public class FieldBatchCounterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Integer checkCount;
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 確認回数を初期化
checkCount = 0;
System.out.println("FieldBatchCounterServletのinitが実行されました");
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// フィールドの確認回数を増加
checkCount++;
// HTMLを出力
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\">");
out.println("<title>フィールドを使った確認回数</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>フィールドを使った確認回数</h1>");
out.println("<p>確認回数:" + checkCount + "</p>");
out.println("<a href=\"FieldBatchCounterServlet\">更新</a>");
out.println("</body>");
out.println("</html>");
}
public void destroy() {
System.out.println("FieldBatchCounterServletのdestroyが実行されました");
}
}この例では、private Integer checkCountというフィールドを用意しています。
initメソッドでcheckCountに0を設定し、doGetメソッドが実行されるたびにcheckCountを1増やしています。
動作確認手順
ブラウザで http://localhost:8080/study/FieldBatchCounterServlet へアクセスします。
ブラウザの表示例

フィールドを使うときの注意点
サーブレットクラスのフィールドには、大きく2つの注意点があります。
注意点
| 注意点 | 内容 |
|---|---|
| ほかのサーブレットやJSPから直接参照できない | フィールドはそのサーブレットインスタンス内の情報だから |
| 同時リクエストで共有される | 複数のユーザーが同じフィールドを同時に使う可能性がある |
まず、フィールドはそのサーブレットクラスの中にある情報です。
そのため、ほかのサーブレットやJSPから直接取得することはできません。
JSPで表示したい情報や、ほかのサーブレットでも使いたい情報は、リクエストスコープ、セッションスコープ、アプリケーションスコープなどに保存するほうが扱いやすくなります。
フィールドは複数リクエストで共有される
サーブレットインスタンスは、2回目以降のリクエストでも再利用されます。
そのため、サーブレット内のフィールドも、複数のリクエストで共有されます。
1人だけが順番にアクセスしているときは、フィールドの値がうまく増えているように見えます。
しかし、複数のユーザーがほぼ同時にアクセスすると、同じフィールドを同時に変更する可能性があります。
同時リクエストで起こり得ること
| 状況 | 起こり得る問題 |
|---|---|
| ユーザーAがcheckCountを取得する | 値が10だった |
| ユーザーBもほぼ同時にcheckCountを取得する | 同じく10だった |
| ユーザーAが11にして保存する | 11になる |
| ユーザーBも11にして保存する | 11のままになる |
| 本来期待する値 | 12 |
| 実際に残る可能性がある値 | 11 |
このように、2人が同時にリクエストしたのに、1人分しか増えない可能性があります。
これは、アプリケーションスコープに保存した値を複数リクエストで同時に更新するときに起こる問題と似ています。
入門段階ではフィールドの変更を避ける
サーブレットクラスのフィールドは、使い方によっては便利に見えます。
しかし、同時リクエストで共有されることを考えると、安易に値を変更するフィールドを使うのは危険です。
特に、ログイン中のユーザー情報、入力途中の内容、個人ごとの状態などをサーブレットのフィールドに保存してはいけません。
フィールドに保存しないほうがよい情報
| 情報 | 理由 |
|---|---|
| ログイン中のユーザー情報 | 複数ユーザーで共有される危険がある |
| 入力途中のフォーム内容 | 他ユーザーの情報と混ざる可能性がある |
| 注文内容や申込内容 | 不整合が大きな問題につながる |
| 正確性が必要なカウンタ | 同時リクエストで値がずれる可能性がある |
入門段階では、サーブレットクラスのフィールドを積極的に使うよりも、スコープを正しく使い分けることを優先しましょう。
ユーザーごとの情報はセッションスコープ、1回のリクエストで使う情報はリクエストスコープ、全体で共有する情報はアプリケーションスコープ、と整理して考えると安全です。
initとdestroyを学ぶとサーブレットの流れが見えてくる
initとdestroyを理解すると、サーブレットがどのように生まれ、どのように使い回され、どのように終わるのかが分かってきます。
doGetやdoPostだけを見ていると、リクエストが来たときの処理に目が向きがちです。
しかし、サーブレットには、インスタンス作成直後の初期化処理と、破棄直前の後片付け処理もあります。
サーブレットのライフサイクル
| 段階 | 実行される処理 |
|---|---|
| インスタンス作成直後 | init |
| リクエスト処理中 | doGetまたはdoPost |
| インスタンス破棄直前 | destroy |
この流れをライフサイクルとして理解しておくと、サーブレットの動作をより正確にイメージできます。
initは最初の一度だけ、doGetやdoPostはリクエストごと、destroyは最後の一度だけです。
この違いを意識しながら、どの処理をどこに書くべきかを考えていきましょう。
