サーブレットはどのように機能しますか?インスタンス化、セッション、シェア変数、マルチスレッド


1144

多数のサーブレットを保持するWebサーバーがあるとします。これらのサーブレット間で受け渡す情報については、セッション変数とインスタンス変数を設定しています。

では、2人以上のユーザーがこのサーバーにリクエストを送信すると、セッション変数はどうなりますか?
それらはすべてのユーザーに共通ですか、それともユーザーごとに異なりますか?
それらが異なる場合、サーバーはどのようにして異なるユーザーを区別できましたか?

同じような質問がもう1つありnます。特定のサーブレットにアクセスするユーザーがいる場合、このサーブレットは最初のユーザーが初めてアクセスしたときにのみインスタンス化されますか、それともすべてのユーザーに対して個別にインスタンス化されますか?
つまり、インスタンス変数はどうなりますか?

回答:


1821

ServletContext

サーブレットコンテナ(Apache Tomcatなど)が起動すると、すべてのWebアプリケーションがデプロイおよびロードされます。Webアプリケーションがロードされると、サーブレットコンテナがServletContext1回作成し、サーバーのメモリに保持します。Webアプリケーションのweb.xmlと付属のすべてweb-fragment.xmlのファイルが解析され、各され<servlet><filter>そして<listener>見つかった(または注釈付きで、各クラス@WebServlet@WebFilterおよび@WebListenerそれぞれ)は一度インスタンス化だけでなく、サーバのメモリに保持されます。インスタンス化されたフィルタごとに、そのinit()メソッドは新しいで呼び出されますFilterConfig

ときにServlet持っている<servlet><load-on-startup>@WebServlet(loadOnStartup)の値よりも大きい0、そのinit()方法は、また、新しいと起動時に呼び出されますServletConfig。それらのサーブレットは、その値で指定されているのと同じ順序で初期化されます(1is 1st、2is 2ndなど)。同じ値が複数のサーブレットのために指定されている場合、それらのサーブレットの各々は、それらが現れるのと同じ順序でロードされるweb.xmlweb-fragment.xmlまたは@WebServletクラスローディング。「load-on-startup」値が存在しない場合、このinit()メソッドは、HTTPリクエストが初めてそのサーブレットにヒットするたびに呼び出されます。

サーブレットコンテナが上記の初期化手順のすべてで終了するServletContextListener#contextInitialized()と、が呼び出されます。

ダウンサーブレットコンテナの閉まりが、それはすべてのWebアプリケーションをアンロードすると、呼び出しdestroy()の初期化サーブレットとフィルタ、およびすべてのすべての方法をServletContextServletFilterおよびListenerインスタンスがゴミ箱に移動されています。最後にServletContextListener#contextDestroyed()が呼び出されます。

HttpServletRequestおよびHttpServletResponse

サーブレットコンテナは、特定のポート番号でHTTPリクエストをリッスンするWebサーバーに接続されます(通常、ポート8080は開発時に使用され、ポート80は本番環境で使用されます)。クライアント(Webブラウザ、または持つ例えば、ユーザ場合は、プログラムで使用してはURLConnection)HTTPリクエストを送信し、サーブレットコンテナは、新規作成HttpServletRequestHttpServletResponseオブジェクトと定義されているすべてのを介してそれらを通過しFilter、最終的には、チェーン内およびServletインスタンスを。

filtersの場合、doFilter()メソッドが呼び出されます。サーブレットコンテナのコー​​ドがを呼び出すchain.doFilter(request, response)と、リクエストとレスポンスは次のフィルタに進みます。フィルタが残っていない場合は、サーブレットにヒットします。

サーブレットの場合、service()メソッドが呼び出されます。デフォルトでは、このメソッドは、にdoXxx()基づいて呼び出すメソッドの 1つを決定しますrequest.getMethod()。決定されたメソッドがサーブレットにない場合、HTTP 405エラーが応答で返されます。

リクエストオブジェクトは、URL、ヘッダー、クエリ文字列、本文など、HTTPリクエストに関するすべての情報へのアクセスを提供します。応答オブジェクトは、HTTP応答を制御して送信する機能を提供します。たとえば、ヘッダーと本文を設定できます(通常、JSPファイルから生成されたHTMLコンテンツを使用)。HTTP応答がコミットされて終了すると、要求オブジェクトと応答オブジェクトの両方がリサイクルされ、再利用できるようになります。

HttpSession

クライアントが初めてwebappにHttpSessionアクセスしrequest.getSession()たり、を介して初めてを取得したりすると、サーブレットコンテナは新しいHttpSessionオブジェクトを作成し、長い一意のID(これはで取得できますsession.getId())を生成して、サーバーのメモリ。サーブレットコンテナはまた、セットCookieSet-CookieしたHTTPレスポンスのヘッダJSESSIONIDの名前とその値として一意のセッションIDとして。

HTTP Cookie仕様(適切なWebブラウザーとWebサーバーが順守する必要がある契約)に従って、クライアント(Webブラウザー)はCookie、Cookieが有効である限り、ヘッダー内の後続のリクエストでこのCookieを送り返す必要があります(つまり、一意のIDは、有効期限が切れていないセッションを参照している必要があり、ドメインとパスは正しいです)。ブラウザーの組み込みHTTPトラフィックモニターを使用して、Cookieが有効であることを確認できます(Chrome / Firefox 23以降/ IE9以降ではF12を押し、[ ネット/ネットワーク ]タブを確認します)。サーブレットコンテナはCookie、すべての着信HTTPリクエストのヘッダーで名前付きのCookieの存在を確認し、JSESSIONIDその値(セッションID)を使用しHttpSessionてサーバーのメモリから関連付けを取得します。

HttpSessionそれはで指定された以上のタイムアウト値のために(すなわち要求で使用されていない)アイドル状態になっているまで生きて滞在<session-timeout>の設定、web.xml。タイムアウト値のデフォルトは30分です。そのため、クライアントが指定された時間よりも長い間Webアプリケーションにアクセスしない場合、サーブレットコンテナはセッションを破棄します。Cookieが指定されていても、以降のすべてのリクエストは同じセッションにアクセスできなくなります。サーブレットコンテナが新しいセッションを作成します。

クライアント側では、ブラウザインスタンスが実行されている限り、セッションCookieは存続します。そのため、クライアントがブラウザインスタンス(すべてのタブ/ウィンドウ)を閉じると、クライアント側でセッションが破棄されます。新しいブラウザインスタンスでは、セッションに関連付けられたCookieは存在しないため、送信されなくなります。これにより、まったくHttpSession新しいセッションCookieが使用されて、まったく新しいものが作成されます。

手短に

  • ServletContext長いWebアプリケーションの命と同じくらいのために命。すべてのセッションのすべてのリクエストで共有されます。
  • HttpSession限り、クライアントが同じブラウザインスタンスを使用したWebアプリケーションと対話している、とのセッションはサーバー側でタイムアウトしていないとのために命。同じセッションのすべてのリクエストで共有されます。
  • HttpServletRequestそしてHttpServletResponse完全な応答(Webページ)が到着するまでサーブレットは、クライアントからのHTTPリクエストを受信した時点からのライブ。それはされていない他の場所で共有しました。
  • すべてServletFilterおよびListenerインスタンスは、Webアプリが存続している限り存続します。それらはすべてのセッションのすべてのリクエストで共有されます。
  • 任意attributeに定義されていることServletContextHttpServletRequestおよびHttpSession質問の生活の中で対象限り生き続けます。オブジェクト自体は、JSF、CDI、SpringなどのBean管理フレームワークの「スコープ」を表します。これらのフレームワークは、スコープがattribute一致するBeanを、最も近い一致スコープとして格納します。

スレッドセーフ

そうは言っても、あなたの主要な懸念はおそらくスレッドの安全性です。これで、サーブレットとフィルターがすべてのリクエスト間で共有されることがわかります。これはJavaのいいところです。マルチスレッドであり、異なるスレッド(読み取り:HTTPリクエスト)が同じインスタンスを使用できます。さもなければ、再作成するにはコストがかかりすぎて、すべてのリクエストに対してそれらは高くinit()なりdestroy()ます。

また、あなたがすべきことを認識すべきである決してとして任意の要求またはセッションスコープのデータを割り当てていないインスタンスのサーブレットまたはフィルタの変数。他のセッションの他のすべてのリクエスト間で共有されます。それスレッドセーフではありません!以下の例はこれを示しています:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

以下も参照してください。


25
それで、クライアントに送信されるJSessionIdを何らかの方法で見つけることができるとき、私は彼のセッションを盗むことができますか?
Toskan、

54
@トスカン:そうです。これは、セッション固定ハックとして知られています。これは、JSP /サーブレットに固有のものではないことに注意してください。Cookieを使用してセッションを維持する他のすべてのサーバー側言語も、PHPSESSIDCookieを使用するPHP、Cookieを使用するASP.NET などのように機密性が高くなりますASP.NET_SessionID;jsessionid=xxx一部のJSP /サーブレットMVCフレームワークが自動的に行うURLの書き換えが不快になるのもこのためです。知らないエンドユーザーが攻撃されないように、セッションIDがURLまたは他の手段でWebページに公開されないことを確認してください。
BalusC 2011年

11
@Toskan:また、WebアプリケーションがXSS攻撃の影響を受けないことを確認してください。つまり、ユーザー制御の入力をエスケープされていない形式で再表示しません。XSSは、すべてのエンドユーザーのセッションIDを収集する方法に門戸を開いています。XSSの背後にある一般的な概念は何ですか?
BalusC 2011年

2
@BalusC、私の愚かさでごめんなさい。これは、すべてのユーザーがthisIsNOTThreadSafeの同じインスタンスにアクセスすることを意味しますか?
陰影2014

4
@TwoThumbSticks 404は、サーブレット全体が存在しない場合に返されます。サーブレットは存在するが、必要なdoXxx()メソッドが実装されていない場合、405が返されます。
BalusC 2017

428

セッション

ここに画像の説明を入力してください ここに画像の説明を入力してください

つまり、Webサーバーは、最初の訪問時に各訪問者に一意の識別子を発行します。訪問者は、次回に認識されるためにそのIDを返却する必要があります。この識別子により、サーバーは、あるセッションが所有するオブジェクトを別のセッションのオブジェクトに対して適切に分離することもできます。

サーブレットのインスタンス化

もしのload-on-startupがある

ここに画像の説明を入力してください ここに画像の説明を入力してください

もしのload-on-startupがある

ここに画像の説明を入力してください ここに画像の説明を入力してください

彼がサービスモードとグルーブに入る同じサーブレットが他のすべてのクライアントからのリクエストで機能します。

ここに画像の説明を入力してください

クライアントごとに1つのインスタンスを作成するのはなぜ良いことではないのですか?これについて考えてください:来たすべての注文に対して1人のピザ男を雇いますか?そうすれば、すぐに廃業するでしょう。

ただし、小さなリスクが伴います。覚えておいてください。この1人の男はすべての注文情報をポケットに入れています。そのため、サーブレットのスレッドセーフティに注意しないと、特定のクライアントに間違った注文を出す可能性があります。


26
あなたの写真は私の理解にとても良いです。質問が1つあります。ピザの注文が多すぎると、このピザレストランはどうなりますか。ピザ屋を1人待つか、ピザ屋をもう1人雇うのですか。感謝します。
zh18

6
彼は次のメッセージを返しますto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords

3
サーブレットは、ピザの配達人とは異なり、同時に複数の配達を行うことができます。彼らはちょうど彼らがクライアントのアドレス、ピザの味を書き留めた場所に特別な注意を払う必要があります...
ブルーノ

42

Javaサーブレットのセッションは、PHPなどの他の言語のセッションと同じです。それはユーザーに固有です。サーバーは、Cookie、URL書き換えなどのさまざまな方法でそれを追跡できます。 Javaドキュメントの記事では、Javaサーブレットのコンテキストでそれを説明し、セッションを維持する方法は、サーバーの設計者が実装の詳細に委ねていることを示しています。この仕様では、サーバーへの複数の接続にわたってユーザーに対して一意である必要があることのみが規定されています。チェックアウトのOracleからこの記事をあなたの質問の両方の詳細については。

編集優れたチュートリアルがあり、ここでサーブレットのセッション内部で作業する方法については。そして、ここでの Javaの彼らが何であるか、サーブレット、およびその使用方法に関する日から章があります。これらの2つの記事の間に、すべての質問に答えられるはずです。


アプリケーション全体でサーブレットコンテキストが1つしかなく、このサーブレットコンテキストを介してセッション変数にアクセスできるので、セッション変数をすべてのユーザーに一意にするにはどうすればよいですか。ありがとう..
Ku Jon

1
servletContextからセッションにどのようにアクセスしていますか?servletContext.setAttribute()を参照していませんか?
matt b

4
@KuJon各Webアプリには1つのServletContextオブジェクトがあります。そのオブジェクトには、ゼロ、1つ、または複数のセッションオブジェクト(セッションオブジェクトのコレクション)があります。各セッションは、他の回答の漫画に見られるように、ある種の識別子文字列によって識別されます。その識別子は、CookieまたはURL書き換えのいずれかによってクライアントで追跡されます。各セッションオブジェクトには独自の変数があります。
バジルブルク2015年

33

サーブレットコンテナ(Apache Tomcatなど)が起動すると、何か問題が発生したり、コンテナ側のコンソールにエラーが表示されたりすると、web.xmlファイル(アプリケーションごとに1つのみ)から読み込まれます。それ以外の場合は、すべてのWebがデプロイおよびロードされます。 web.xml(デプロイメント記述子と呼ばれる)を使用したアプリケーション。

サーブレットのインスタンス化フェーズでは、サーブレットインスタンスの準備はできていますが、次の2つの情報がないため、クライアントリクエストを処理できません
。1:コンテキスト情報
2:初期構成情報

サーブレットエンジンは、上記の不足している情報をカプセル化したservletConfigインターフェースオブジェクトを作成し、サーブレットエンジンは、サーブレットのservletConfigオブジェクト参照を引数として指定することにより、サーブレットのinit()を呼び出します。init()が完全に実行されると、サーブレットはクライアント要求を処理する準備が整います。

Q)サーブレットの存続期間中に、インスタンス化と初期化が何回行われますか?

A)1回だけ(すべてのクライアント要求に対して新しいスレッドが作成されます)、サーブレットの1つのインスタンスのみが任意の数のクライアント要求を処理します。つまり、1つのクライアント要求を処理した後、サーバーは停止しません。他のクライアント要求を待ちます。つまり、サーブレットでCGI(クライアント要求ごとに新しいプロセスが作成される)の制限が克服されます(内部的にサーブレットエンジンがスレッドを作成します)。

Q)セッションのコンセプトはどのように機能しますか?

A)HttpServletRequestオブジェクトでgetSession()が呼び出されたとき

ステップ1:リクエストオブジェクトは、着信セッションIDについて評価されます。

ステップ2:IDが使用できない場合、新しいHttpSessionオブジェクトが作成され、それに対応するセッションID(つまりHashTableの)が生成されます。セッションIDはhttpservlet応答オブジェクトに格納され、HttpSessionオブジェクトの参照がサーブレットに返されます(doGet / doPost) 。

ステップ3:IDが利用可能な新しいセッションオブジェクトが作成されていない場合、リクエストからセッションIDが取得されます。オブジェクトの検索は、セッションIDをキーとして使用して、セッションのコレクションで行われます。

検索が成功すると、セッションIDがHttpServletResponseに格納され、既存のセッションオブジェクト参照がUserDefineservletのdoGet()またはdoPost()に返されます。

注意:

1)サーブレットのコードからクライアントに制御が渡るとき、セッションオブジェクトがサーブレットコンテナ、つまりサーブレットエンジンによって保持されていることを忘れないでください。

2)マルチスレッドはサーブレット開発者の実装に任されています。つまり、クライアントの複数のリクエストを処理して、マルチスレッドコードを気にする必要はありません。

短い形式:

サーブレットは、アプリケーションが起動したとき(サーブレットコンテナにデプロイされたとき)、またはサーブレットがインスタンス化されたときに(load-on-startup設定に応じて)最初にアクセスされたときに作成され、サーブレットのinit()メソッドが呼び出されます。次に、サーブレット(その唯一のインスタンス)がすべての要求を処理します(そのservice()メソッドが複数のスレッドによって呼び出されます)。そのため、同期をとることはお勧めできません。アプリケーションがアンデプロイされる(サーブレットコンテナーが停止する)ときは、サーブレットのインスタンス変数を避け、destroy()メソッドを呼び出します。


20

セッション -クリス・トンプソンが言ったこと。

インスタンス化 -サーブレットは、サーブレットがマッピングされた最初のリクエストを受信したときにインスタンス化されます(サーブレットがの<load-on-startup>要素で起動時にロードするように構成されている場合を除くweb.xml)。同じインスタンスを使用して、後続のリクエストを処理します。


3
正しい。追加の考え:各リクエストは、その単一のサーブレットインスタンスで実行するための新しい(またはリサイクルされた)スレッドを取得します。各サーブレットには1つのインスタンスがあり、多くのスレッドがあります(多数の同時リクエストの場合)。
バジルブルク2015年

13

サーブレット仕様JSR-315は、サービス(およびdoGet、doPost、doPutなど)メソッドでのWebコンテナの動作を明確に定義しています(2.3.3.1マルチスレッドの問題、9ページ)。

サーブレットコンテナは、サーブレットのサービスメソッドを通じて同時要求を送信できます。リクエストを処理するには、サーブレット開発者は、サービスメソッドで複数のスレッドを使用して並行処理を行うための適切な準備を行う必要があります。

お勧めしませんが、Developerの代替手段は、SingleThreadModelインターフェースを実装することです。これにより、コンテナーは、サービスメソッド内で一度に1つの要求スレッドのみが存在することを保証する必要があります。サーブレットコンテナは、サーブレットに対する要求をシリアル化するか、サーブレットインスタンスのプールを維持することにより、この要件を満たすことができます。サーブレットが、配布可能としてマークされているWebアプリケーションの一部である場合、コンテナは、アプリケーションが分散されている各JVM内のサーブレットインスタンスのプールを維持できます。

SingleThreadModelインターフェースを実装していないサーブレットの場合、サービスメソッド(またはHttpServlet抽象クラスのサービスメソッドにディスパッチされるdoGetやdoPostなどのメソッド)がsynchronizedキーワードで定義されている場合、サーブレットコンテナはインスタンスプールアプローチを使用できません。 、しかしそれを通してリクエストをシリアライズする必要があります。パフォーマンスに悪影響を与えるため、これらの状況では、開発者がサービスメソッド(またはそれにディスパッチされたメソッド)を同期しないことを強くお勧めします。


2
FYI、現在のサーブレット仕様(2015-01)は3.1で、JSR 340によって定義されています。
バジルブルク


1
とてもきちんとした答え!あずきっく
トムテイラー

0

上記の説明から明らかなように、SingleThreadModelを実装することによりことにより、サーブレットはサーブレットコンテナによってスレッドの安全性を確保できます。コンテナの実装では、次の2つの方法でこれを実行できます。

1)単一インスタンスへの要求(キューイング)のシリアル化-これは、サーブレットがSingleThreadModelを実装していないのに似ていますが、サービス/ doXXXメソッドを同期します。または

2)インスタンスのプールを作成する-これは、サーブレットをホストしている環境の制限的なパラメータ(メモリ/ CPU時間)と比べて、サーブレットの起動/初期化作業/時間の間のトレードオフであり、より優れたオプションです。


-1

いいえ。サーブレットはスレッドセーフでません

これにより、一度に複数のスレッドにアクセスできます

サーブレットをスレッドセーフにしたい場合は、

Implement SingleThreadInterface(i) これは空のインターフェースです

方法

または、同期メソッドに行くことができます

同期を使用して、サービスメソッド全体を同期させることができます

メソッドの前のキーワード

例::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

または、コードのブロックをSynchronizedブロックに置くことができます

例::

Synchronized(Object)

{

----Instructions-----

}

メソッド全体を作るよりも同期ブロックの方がいいと思います

同期済み

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.