サーブレット&JSPの基本|アプリケーションスコープの注意点とスコープ比較

全ユーザーで共有できるからこそ注意が必要。アプリケーションスコープの落とし穴とスコープの使い分けを理解しよう

アプリケーションスコープは、Webアプリケーション全体でインスタンスを共有できる便利なスコープです。

リクエストスコープは1回のリクエスト内、セッションスコープはユーザーごと、アプリケーションスコープはWebアプリケーション全体で使う、という違いがありました。

前回作成した評価ボタン機能では、教材ページに対して役に立ったやもう少しをクリックした人数を、アプリケーションスコープに保存しました。評価数は特定のユーザーだけの情報ではなく、すべてのユーザーで共有したい情報なので、アプリケーションスコープに向いているように見えます。

しかし、アプリケーションスコープは便利な反面、扱い方を間違えるとトラブルの原因になります。

特に注意したいのが、同時アクセスによる不整合と、インスタンスの保存期間です。

この記事では、評価ボタン機能を例にしながら、アプリケーションスコープで起こりやすい問題を確認します。そのうえで、リクエストスコープ、セッションスコープ、アプリケーションスコープの違いを整理し、どの場面でどのスコープを選べばよいのかを考えていきます。

アプリケーションスコープは全ユーザーで共有される

アプリケーションスコープは、1つのWebアプリケーションにつき1つだけ作成されます。

そのため、同じWebアプリケーションを利用しているすべてのユーザーが、同じアプリケーションスコープを参照します。

たとえば、PageFeedbackインスタンスをpageFeedbackという属性名でアプリケーションスコープに保存した場合、青山さんがアクセスしても、山田さんがアクセスしても、同じPageFeedbackインスタンスを取得できます。

アプリケーションスコープの共有イメージ

利用者取得するインスタンス
青山さんアプリケーションスコープ内のpageFeedback
山田さんアプリケーションスコープ内のpageFeedback
佐藤さんアプリケーションスコープ内のpageFeedback

このように、全ユーザーで同じインスタンスを使うため、共通のお知らせや全体の評価数などには利用しやすいスコープです。

一方で、全ユーザーが同じインスタンスを使うということは、複数のユーザーが同じタイミングでそのインスタンスを更新する可能性がある、ということでもあります。

注意したいトラブル

アプリケーションスコープで特に注意したい点は、次の2つです。

アプリケーションスコープ利用時の注意点

注意点内容
同時アクセスによる不整合複数のリクエストが同じインスタンスを同時に更新すると、値がずれることがある
インスタンスの保存期間Webアプリケーションが終了すると消え、削除しなければメモリに残り続ける

評価ボタン機能のように、数値を増やす処理では、同時アクセスによる不整合が起こりやすくなります。

また、アプリケーションスコープにはセッションスコープのようなタイムアウトがありません。不要なインスタンスを保存したままにすると、メモリを圧迫する原因になります。

図1:アプリケーションスコープで共有される評価情報

この図から分かること

アプリケーションスコープに保存したインスタンスは、Webアプリケーション全体で共有されます。

青山さん、山田さん、佐藤さんがそれぞれ別のブラウザからアクセスしても、同じpageFeedbackインスタンスを取得します。

この性質により、教材ページの評価数のように、全ユーザーで共通して見たい情報を扱えます。ただし、全員が同じインスタンスを利用するため、同時に更新が行われると値の不整合が起こる可能性があります。

注意点① 同時アクセスによる不整合

アプリケーションスコープに保存されているインスタンスを、複数のリクエストがほぼ同じタイミングで更新すると、不整合が起こる場合があります。

たとえば、教材ページ評価ボタン機能で、役に立ったの数が10人だったとします。

そこへ、青山さんと山田さんがほぼ同時に役に立ったをクリックしたとします。

本来であれば、10人から2人分増えて12人になってほしいところです。

しかし、処理のタイミングによっては、11人にしかならないことがあります。

なぜ1人分しか増えないのか

評価数を増やす処理では、次のような流れが行われます。

評価数を増やす基本の流れ

順番内容
1アプリケーションスコープからPageFeedbackを取得する
2現在の役に立ったの数を取得する
3取得した数に1を足す
4更新後の値をPageFeedbackに設定する
5アプリケーションスコープに保存する

1人だけがクリックする場合は、この流れで問題なく動きます。

しかし、2人がほぼ同じタイミングでクリックすると、どちらの処理も更新前の10を取得してしまうことがあります。

その結果、青山さんの処理も10から11に更新し、山田さんの処理も10から11に更新します。

最終的に保存される値は11になり、2人がクリックしたのに1人分しか増えていないように見えてしまいます。

同時アクセスで起こるずれ

タイミング青山さんの処理山田さんの処理
最初の状態役に立ったは10人役に立ったは10人
取得10を取得する10を取得する
加算10に1を足して11にする10に1を足して11にする
保存11を保存する11を保存する
結果2回クリックされたのに11人になる期待値は12人

このように、同じインスタンスを複数のリクエストが同時に更新すると、更新のタイミングが重なり、期待した結果にならない場合があります。

図2:ほぼ同時にクリックされると不整合が起こる

この図から分かること

アプリケーションスコープに保存されたインスタンスは、すべてのユーザーで共有されます。

そのため、複数のユーザーがほぼ同時に評価ボタンをクリックすると、同じPageFeedbackインスタンスを同時に更新することがあります。

どちらの処理も更新前の値を取得してしまうと、2人がクリックしても1人分しか増えないことがあります。これは、同時アクセスによって更新処理が競合している状態です。

不整合が問題になるデータは保存しない

同時アクセスによる不整合が大きな問題になるデータは、アプリケーションスコープに保存しないほうが安全です。

評価ボタンのような簡単なサンプルでは、1人分のずれが学習用として許容できる場合もあります。

しかし、在庫数、残席数、決済金額、申込人数の上限など、正確な更新が求められるデータでは、不整合が致命的な問題になることがあります。

アプリケーションスコープでの更新に注意が必要な情報

情報注意が必要な理由
在庫数同時購入で数がずれると販売数に影響する
残席数同時申込で定員を超える可能性がある
金額更新のずれが直接トラブルにつながる
重要な集計値正確な数値が必要になる

このような重要なデータは、アプリケーションスコープではなく、ファイルやデータベースなど、適切な管理方法を使うことを考えます。

また、アプリケーションスコープを使う場合でも、保存は一度だけにして、その後は更新せずに取得だけにする使い方であれば、不整合のリスクを減らせます。

更新が必要な場合は競合への対応が必要

アプリケーションスコープに保存されたインスタンスをどうしても更新する必要がある場合は、同時アクセスによる競合に対応する必要があります。

Webアプリケーションでは、リクエストごとに処理が実行されます。複数のリクエストが同時に届くと、それぞれの処理が並行して進むことがあります。

この処理の単位をスレッドと考えると分かりやすいです。

スレッドとして考える例

処理内容
青山さんのリクエスト処理1つのスレッドとして実行される
山田さんのリクエスト処理別のスレッドとして実行される
同じインスタンスの更新複数のスレッドが同時に触る可能性がある

同じインスタンスを複数のスレッドが同時に更新すると、値のずれが起こることがあります。

そのため、どうしても更新が必要な処理では、スレッドによる競合を調停する仕組みが必要になります。

ただし、この段階では、アプリケーションスコープに保存したインスタンスを複数ユーザーが同時に更新すると不整合が起こる場合がある、という考え方をしっかり押さえておきましょう。

注意点② インスタンスの保存期間

アプリケーションスコープに保存したインスタンスは、Webアプリケーションが終了すると消滅します。

ブラウザを閉じても、アプリケーションスコープの内容はすぐには消えません。

しかし、サーバを停止したり再起動したり、Webアプリケーションが終了したりすると、アプリケーションスコープに保存されていたインスタンスは利用できなくなります。

アプリケーションスコープの保存期間

操作アプリケーションスコープの内容
同じWebアプリケーションが動き続けている保存されたインスタンスを取得できる
ブラウザを閉じる基本的にはアプリケーションスコープの内容は残る
Webアプリケーションを終了する保存されたインスタンスは消滅する
サーバを停止または再起動する保存されたインスタンスは消滅する

アプリケーションスコープは、Webアプリケーションが動いている間だけ利用できる保存領域です。

Webアプリケーションを再開したあとも使いたいデータは、アプリケーションスコープではなく、ファイルやデータベースなどアプリケーションの外に保存する必要があります。

長期間残したいデータは外部に保存する

アプリケーションスコープは、サーバのメモリ上にインスタンスを保存します。

そのため、Webアプリケーションが終了すると内容は消えます。

評価数のように、サーバを再起動しても残したいデータを扱う場合は、アプリケーションスコープだけでは不十分です。

保存先の考え方

保存したい期間向いている保存先
1回のリクエスト中だけリクエストスコープ
ユーザーの操作中だけセッションスコープ
Webアプリケーション起動中だけアプリケーションスコープ
Webアプリケーション終了後も残したいファイルやデータベース

アプリケーションスコープは、永続的な保存場所ではありません。

Webアプリケーションが終了しても残したいデータは、アプリケーションの外に保存する必要があります。

アプリケーションスコープにはタイムアウトがない

セッションスコープにはセッションタイムアウトがあります。

一定時間利用されていないセッションは、サーバによって破棄されます。

一方、アプリケーションスコープには、セッションスコープのようなタイムアウトはありません。

そのため、アプリケーションスコープに保存したインスタンスは、削除するかWebアプリケーションが終了するまで残ります。

セッションスコープとの違い

項目セッションスコープアプリケーションスコープ
主な単位ユーザーごとWebアプリケーションごと
タイムアウトあるない
削除される主なタイミングセッションタイムアウト、削除、破棄削除、Webアプリケーション終了
残り続ける危険ユーザーごとの情報が残るアプリ全体の情報が残る

大量のインスタンスをアプリケーションスコープに保存したままにすると、メモリを圧迫し続けます。

不要になったインスタンスは、removeAttributeを使って明示的に削除することを考えます。

図3:スコープの保存期間と共有範囲の違い

この図から分かること

リクエストスコープ、セッションスコープ、アプリケーションスコープは、保存できる範囲と期間が異なります。

リクエストスコープは1回のリクエスト中だけ利用します。セッションスコープはユーザーごとに情報を保存します。アプリケーションスコープはWebアプリケーション全体で情報を共有します。

同じsetAttributeやgetAttributeを使っても、どのスコープを使うかによって、保存したインスタンスを誰が、いつまで利用できるかが変わります。

スコープを比較する

ここまで、リクエストスコープ、セッションスコープ、アプリケーションスコープを学習してきました。

それぞれのスコープは、インスタンスを保存できる点では共通しています。

ただし、作成される単位、利用できる期間、リクエストをまたげるかどうか、ユーザーごとの情報を保存できるかどうかが違います。

各スコープの特徴

項目リクエストスコープセッションスコープアプリケーションスコープ
インスタンスHttpServletRequestHttpSessionServletContext
暗黙オブジェクトrequestsessionapplication
作成される単位リクエストごとユーザーごと、ブラウザごとアプリケーションごと
保存したインスタンスが取得できる期間削除するか、レスポンスするまで削除するか、セッションタイムアウトするまで削除するか、アプリケーションが終了するまで
リクエストをまたいでインスタンスを保存できないできるできる
ユーザーごとのインスタンスを保存できる、ただしリクエストごとに保存するできるできない、全ユーザーで共有する

この表を見ると、3つのスコープは似ているようで、かなり性質が違うことが分かります。

スコープを選ぶときは、保存したいインスタンスを誰が使うのか、いつまで使うのかを考えることが大切です。

リクエストスコープが向いている場面

リクエストスコープは、1回のリクエストの中だけで使うデータに向いています。

たとえば、サーブレットで処理した結果をJSPへ渡して表示するだけなら、リクエストスコープで十分です。

リクエストスコープが向いている情報

情報理由
入力結果の一時表示レスポンス後に残す必要がない
検索結果の表示その画面表示だけで使う
エラーメッセージフォワード先のJSPで表示できればよい
診断結果1回の結果画面で表示できればよい

リクエストスコープは、レスポンスが返ると使えなくなります。

そのため、次のリクエストでも同じ情報を使いたい場合には向いていません。

セッションスコープが向いている場面

セッションスコープは、ユーザーごとに情報を保存したい場合に向いています。

ログイン中のユーザー情報や、入力画面から確認画面、完了画面へ進むときの一時的な情報などに使います。

セッションスコープが向いている情報

情報理由
ログイン中のユーザー情報ユーザーごとに分ける必要がある
入力から確認まで保持する情報リクエストをまたいで使いたい
個人の操作状態他のユーザーと共有してはいけない
一連の画面遷移で使うデータ同じユーザーの操作中だけ保持したい

セッションスコープはリクエストをまたげますが、全ユーザーで共有する情報には向いていません。

ユーザーごとの情報を扱う場合に利用します。

アプリケーションスコープが向いている場面

アプリケーションスコープは、Webアプリケーション全体で共有したい情報に向いています。

全ユーザーで同じ値を見たり、同じインスタンスを利用したりする場合に使います。

アプリケーションスコープが向いている情報

情報理由
共通のお知らせ全ユーザーに同じ内容を表示したい
教材ページの評価数全ユーザーで同じ数を共有したい
アプリケーション共通の設定情報複数のサーブレットやJSPで共通利用したい
起動中だけ使う共有データWebアプリケーションが動いている間だけ利用したい

アプリケーションスコープは、ユーザーごとに分ける必要がある情報には向いていません。

また、同時に更新される可能性がある重要なデータを保存する場合は、不整合が起こらないように注意が必要です。

スコープ選びの考え方

スコープを選ぶときは、次の順番で考えると整理しやすくなります。

スコープ選びの目安

質問選ぶスコープ
その画面表示だけで使えればよいかリクエストスコープ
同じユーザーの次の画面でも使いたいかセッションスコープ
すべてのユーザーで共有したいかアプリケーションスコープ
Webアプリケーション終了後も残したいかファイルやデータベース

スコープは、何となく選ぶのではなく、保存したいデータの性質に合わせて選びます。

どの範囲で使うのか、いつまで残したいのか、誰に見せるのかを考えると、適切なスコープを選びやすくなります。

ページスコープについて

スコープには、リクエストスコープ、セッションスコープ、アプリケーションスコープのほかに、ページスコープもあります。

ページスコープは、1つのJSPページの中で利用されるスコープです。

ただし、ServletとJSPを使った基本的なWebアプリケーション開発では、ページスコープを意識的に使う場面は多くありません。

そのため、まずはリクエストスコープ、セッションスコープ、アプリケーションスコープの3つをしっかり理解することが大切です。

まず押さえたい3つのスコープ

優先して理解したいスコープ理由
リクエストスコープサーブレットからJSPへデータを渡す基本になる
セッションスコープユーザーごとの情報をリクエストをまたいで扱える
アプリケーションスコープWebアプリケーション全体で共有する情報を扱える

この3つを使い分けられるようになると、ServletとJSPで作れるWebアプリケーションの幅が広がります。

コンテキストとは

ServletContextのContextは、Webアプリケーションを表します。

WebアプリケーションをリクエストするURLでは、サーバ名のあとにコンテキストの名前を指定します。

URLの基本形は次のように考えます。

http://サーバ/コンテキストの名前/...

たとえば、動的Webプロジェクト名がstudyの場合、デフォルトではコンテキストの名前もstudyになります。

そのため、LessonIndexを実行する場合は、次のようなURLになります。

http://localhost:8080/study/LessonIndex

URLとコンテキストの関係

URLの部分内容
localhost:8080サーバとポート番号
studyコンテキストの名前
LessonIndexサーブレットのURLパターン

デフォルトでは、動的Webプロジェクトの名前がそのままコンテキスト名になります。

そのため、プロジェクト名studyで作成したWebアプリケーションでは、URLにもstudyが入ります。

ServletContextはWebアプリケーション全体を表す

ServletContextは、Webアプリケーション全体を表すオブジェクトです。

アプリケーションスコープを操作するときにServletContextを使うのは、アプリケーションスコープがWebアプリケーション全体に属しているからです。

ServletContextの考え方

項目内容
Contextの意味Webアプリケーション
ServletContextWebアプリケーション全体を表すインスタンス
アプリケーションスコープServletContextを通して操作する
JSPでの暗黙オブジェクトapplication

サーブレットではgetServletContextでServletContextを取得します。

JSPでは暗黙オブジェクトapplicationを使ってアプリケーションスコープを操作できます。

このように、ServletContextはアプリケーションスコープの理解に関わる大切な考え方です。

アプリケーションスコープで確認したいこと

アプリケーションスコープを使うときは、次の点を確認しましょう。

確認項目内容
全ユーザーで共有してよい情報か個人ごとの情報を保存していないか確認する
同時更新されても問題ないか競合による不整合が致命的にならないか確認する
更新が本当に必要か保存は一度だけにして取得中心にできないか考える
長期保存が必要かWebアプリケーション終了後も残したいなら外部保存を考える
不要なインスタンスを削除しているかremoveAttributeで整理する
サーバ再起動で消えることを理解しているかアプリケーションスコープは永続保存ではない
ほかのスコープと使い分けているかrequest、session、applicationの範囲を意識する

アプリケーションスコープは、全ユーザーで共有できる便利なスコープです。

ただし、共有範囲が広い分、同時アクセスや保存期間には注意が必要です。

ServletとJSPでWebアプリケーションを作るときは、リクエストスコープ、セッションスコープ、アプリケーションスコープの違いを理解し、データの性質に合わせて使い分けましょう。