HttpSession
Java EE 6で定義されているように、インターフェースは、ユーザーアクションのグループ化、ユーザーによって実行された継続的なアクションの識別、および複数のページ要求にわたるそのユーザーに関する情報の保存の方法を提供します。
総括
セッションは明らかに多くのリクエスト間で共有されるため、スレッドセーフの問題とその影響が生じます。サーブレット3.0仕様のセッションに関する第7章では、このような問題に対処することの重要性がサポートされている場合があります。
リクエストスレッドを実行する複数のサーブレットは、同じセッションオブジェクトに同時にアクティブにアクセスできる場合があります。コンテナは、セッション属性を表す内部データ構造の操作がスレッドセーフな方法で実行されることを保証する必要があります。開発者は、属性オブジェクト自体にスレッドセーフにアクセスする責任があります。これにより、HttpSessionオブジェクト内の属性コレクションが同時アクセスから保護され、アプリケーションがそのコレクションを破損させる可能性がなくなります。
サーブレット3.0仕様(JSR-315)、ch。7.7.1、私の強調。
しかし、このすべての混乱はどこから生じているのでしょうか?AJAX以前のWebアプリケーションの時代には、同じユーザーがセッションにアクセスする可能性はほとんどなかったため、開発者は同期についてあまり気にしませんでした。しかし、AJAX対応のWebアプリケーションを構築する傾向が高まっているため、同じユーザーからの2つの要求が同時に発生し、セッションに同時にアクセスする可能性が非常に高くなります。
ちなみに、サーブレットのインターフェースを使用することで、スレッドの問題をわずかに減らすことができますSingleThreadModel
が、その適用の必要性については議論があります(サーブレット3.0仕様(JSR-315)、ch。2.2.1)。さらに、サーブレット3.0および「HttpSessionのインスタンスなど、一度に複数のサーブレットインスタンスにアクセスできるオブジェクトは、SingleThreadModelを実装するサーブレットを含む複数のサーブレットでいつでも利用できる可能性がある」ため、非推奨になりました。
同期に関する問題
Java EE 6チュートリアルでは、「複数のWebコンポーネントがセッションに格納されているオブジェクトにアクセスすると、同時アクセスが発生する可能性がある」と明示的に述べられています(Java EE 6チュートリアル、第II-15章)。HttpSession
さらに、インターフェースを詳しく見ると、オブジェクトをセッションにバインドできるいくつかのメソッドが見つかります。これにより、複数のユーザー接続間でユーザー情報を保存できるため、HTTPプロトコルのステートレス性が克服されます。これらのメソッドは次のとおりです。
getAttribute(String name)
(および現在非推奨になっているgetValue(String name)
);
setAttribute(String name, Object value)
(そして現在非推奨になっているputValue(String name, Object value)
;
removeAttribute(String name)
(および現在非推奨になっているremoveValue(String name)
);
invalidate()
と
- インターフェイスの他のメソッド。
最後のメソッドは、このセッションを無効にし、ユーザー情報を保持しているオブジェクトをバインド解除します。したがって、これは私たちが恐れていることではありません。最も重要なメソッドは、Object
セッションから/セッションへのsの読み取り、書き込み、および削除を行うメソッドです。これらのメソッドは、異なるスレッドによって同時にデータを処理/アクセスするために呼び出されるためです。
Brian GoetzがJavaの理論と実践で述べているように、同時アクセスの明らかな問題は次のとおりです。すべてのステートフルWebアプリケーションが壊れていますか?:
- アトミック障害。1つのスレッドが複数のデータを更新し、別のスレッドが一貫性のない状態にあるときにデータを読み取り、
- 読み取りスレッドと書き込みスレッドの間の可視性の失敗。一方のスレッドはデータを変更しますが、もう一方のスレッドは古いデータまたは一貫性のない状態のデータを確認します。
問題の簡単な解決策
彼は後に、Webアプリケーション内の並行性の問題を減らす5つの手法を提案し、最後に「HttpSessionで要求をシリアル化すると、多くの並行性の危険がなくなる」と述べています。Marty Hallは、セッショントラッキングに関するオンラインチュートリアルで、同期について次のように提案しています。「同期されたブロックのラベルとして、セッションまたはおそらくセッションの値を使用してください」。したがって、基本的な設定は次のとおりです。
HttpSession session = request.getSession();
synchronized(session) {
SomeClass value = (SomeClass)session.getAttribute("someID");
if (value == null) {
value = new SomeClass(...);
}
doSomethingWith(value);
session.setAttribute("someID", value);
}
この設定では、セッションにアクセスするための重複するリクエストが同期されます。
スレッドセーフでない使用法の例
HttpSession session = request.getSession();
MyClass myClass = (MyClass)session.getAttribute("myClass");
if(myClass != null) {
myClass.performOperation();
session.setAttribute("myClass", myClass);
}
JSFでのセッションによる明示的な操作の必要性
セッションオブジェクト内のデータの操作が同時実行の問題につながる可能性があることは明確に理解されています。さらに、セッションオブジェクトを暗黙的に管理するJSFフレームワーク内で開発することを選択した場合、その適用性は疑わしいものになります。
結局、オブジェクトが本質的にそこに属している場合は、オブジェクトをセッションに入れることになっています。開発者は、問題を解決する手段としてオブジェクトをセッションに配置する傾向がありますが、通常はより良い方法があります。いくつかの間違いは、記事JSFベストプラクティス: ThomasAselによるクリーンセッションのスコープ管理でカバーされています。
スレッドの問題を軽減するいくつかの方法
スレッドの問題のほとんどは、HttpSession
が間違った方法で使用された場合に発生します。この観点から、スレッディングの問題はスコーピングの問題の結果です。
たとえば、より狭いスコープ、つまりリクエストスコープまたはビュースコープに属すると想定されるセッションに値を設定すると、コードは有形の確率でcuncurrencyの問題に対して脆弱になります。代わりに、すべてのセッションデータが適切なスコープに属している場合、ユーザーが同時実行の問題に直面する可能性は非常に低くなります。
@SessionScoped
JSFが最終的にそれらのBeanを、管理対象Beanの名前をキーとしての属性として格納するとすぐに、 Beanスレッドの安全性を開発者が確保する必要があります。HttpSession
名前によるマネージドBeanへのアクセスは、JSFでは便利であり、私が知る限り、によって行われるカバーの下にありますsession.getAttribute("managedBeanName")
。
このコンテキストでは、ユーザーがセッションに正しいデータを配置し、それを正しく管理した場合(つまり、セッションを妨害せずに解決する必要のある問題を解決しなかった場合)、私が見る唯一の欠点はBalusCの回答に記載されています。 「密結合と悪い設計」。それ以外の場合(JSFがカバーの下でBeanのライフサイクルを正しく管理し、設計上の問題が発生することを省略した場合)、使用法は類似しています。
頭に浮かぶ並行性の問題を引き起こすスコープの問題のいくつかの例:
- セッション中のユーザー情報(ユーザー認証の詳細、ユーザー設定、国際化されたWebアプリケーションでのロケールの選択)に関連するもの以外のものの保存。
- ビュー間での情報の共有は、セッションを使用して行うべきではありません。データの取得はリクエストによって行う
GET
必要があり、データの操作はリクエストによって行う必要がありPOST
ます。ページ転送中にデータを渡すことは、たとえば、を使用して<f:setPropertyActionListener>
行うことができ、ページリダイレクト中にデータを渡すことは、たとえば、#{flash}
属性を使用して行うことができる。
- 繰り返されるコンポーネントのセットアップで情報を渡すことは、セッションを妨害することなく、、、、および情報の取得を介し
<f:attribute>
て、<f:setPropertyActionListener>
アクション<f:param>
(リスナー)メソッド<f:viewParam>
などを介して実行する必要があります。
要約すると、上記のリストに厳密に従わないと、もちろんいつか誤った出力が表示されますが、セッションスコープに属する適切なデータを選択した場合、問題はほとんど発生しません。通常、このデータの選択は、ユーザーセッションの質問全体で必要な情報に答えるときに行うことができます。
そうは言っても、よくある間違いは、情報をセッションに入れて後続のビューで利用できるようにし、その情報を取得してからセッションから削除することです。これは明らかに間違っています。私は、このアプローチがあなたの対話者によって採用されたと強く信じています。
結局、JSFは開発タスクを容易にするために進化しているので、その便利な機能を使用してみませんか?