ステートレスおよびステートフルエンタープライズJava Bean


93

私はJava EE 6チュートリアルを行っており、ステートレスセッションBeanとステートフルセッションBeanの違いを理解しようとしています。ステートレスセッションBeanがメソッド呼び出し間で状態を保持しない場合、なぜ私のプログラムはそのように動作するのですか?

package mybeans;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

クライアント

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import mybeans.MyBean;
import java.io.PrintWriter;

@WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
public class ServletClient extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

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

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

getNumberが毎回0を返すことを期待していましたが、1を返し、ブラウザーでのサーブレットのリロードによってさらに増加し​​ます。問題はもちろん、ステートレスセッションBeanがライブラリやアプリケーションサーバーではなく、どのように機能するかを理解していることです。ステートフルに変更したときに動作が異なるステートレスセッションBeanの簡単なhello worldタイプの例を誰かに教えてもらえますか?


6
関連:stackoverflow.com/questions/8887140/…この答えは、おそらく理解するのが簡単です。。サーブレットは、基本的にアプリケーションが(共有/すべてのHTTP要求/セッションにわたって再利用されるだけ1サーブレット・インスタンスのアプリケーションにわたるがありますスコープあることに注意してください
BalusC

こんにちは、あなたが最初の増分を行い、そしてあなたが0の値を期待することはできませんので、その後....値を取得
rzur2004

これを聞いてくれてありがとう、それが今の私の問題に対処します。私はそれをもっとよく尋ねることはできませんでした
kholofelo Maloma 2015年

回答:


93

重要な違いは、プライベートメンバー変数ではなく、状態を特定のユーザーに関連付けることです(「ショッピングカート」と考えてください)。

ステートフルセッションBeanのステートフル部分は、サーブレットのセッションに似ています。ステートフルセッションBeanを使用すると、ウェブクライアントがなくても、アプリでそのセッションを維持できます。アプリサーバーがステートレスセッションBeanをオブジェクトプールからフェッチすると、特定のユーザーに関連付けられていないため、それを使用してANYリクエストを満たすことができることがわかります。

ステートフルセッションBeanは、最初にそれを取得したユーザーに配布する必要があります。これは、ショッピングカートの情報が彼らだけに知られているためです。アプリサーバーは、これがそうであることを保証します。ショッピングを開始でき、アプリサーバーがステートフルセッションBeanを私に渡したときに、アプリがどれほど人気が​​あるか想像してみてください。

したがって、プライベートデータメンバーは確かに「状態」ですが、「ショッピングカート」ではありません。(非常に良い)サンプルをやり直して、インクリメントされる変数が特定のユーザーに関連付けられるようにしてください。それをインクリメントし、新しいユーザーを作成して、インクリメントされた値が引き続き表示されるかどうかを確認します。正しく行われれば、すべてのユーザーが自分のバージョンのカウンターのみを表示するはずです。


コメントで明確な回答を提供できますか?この例のステートレスBeanが常に値を保持し、毎回値を増やすのはなぜですか?ユーザーが1人しかいないからですか?
arjacsoh 2013年

2
カウンターは、ユーザー数に関係なく増加します。したがって、user1が入ってカウンターを1にインクリメントし、同時にuser2が入ってインクリメントする場合、値は2になります。実際には、user1が1でuser2が1であることを示す必要があります(それが目的の場合)。ショッピングカート上記の例)。
クリシュナ

137

ステートレスセッションBean(SLSB)は1つのクライアントに関連付けられておらず、1つのクライアントが各メソッド呼び出しで同じインスタンスを取得する保証はありません(一部のコンテナーは、各メソッド呼び出しセッションでBeanを作成および破棄する場合があります。これは実装固有の決定です) 、しかしインスタンスは通常プールされます-そして私はクラスター環境については言及しません)。つまり、ステートレスBeanにはインスタンス変数が含まれる場合がありますが、これらのフィールドは1つのクライアントに固有ではないため、リモート呼び出し間でそれらに依存しないでください。

これとは対照的に、ステートフルセッションBean(SFSB)がされている専用の彼らの全体の生活のために一つのクライアントに、そこには交換されていないか(資源を節約するために不動態化後にメモリから追い出されてもよいが、それはまた別の話だ)インスタンスをプールし、対話状態を維持します。つまり、Beanのインスタンス変数は、メソッド呼び出し間でクライアントに関連するデータを保持できます。また、これにより、相互に依存するメソッド呼び出しが可能になります(1つのメソッドによって行われた変更は、後続のメソッド呼び出しに影響します)。SFSBの典型的な使用例は、複数ステップのプロセス(登録プロセス、ショッピングカート、予約プロセスなど)です。

もう一つ。SFSBを使用している場合は、サーブレットやJSFマネージドBeanなど、本質的にマルチスレッドのクラスにそれら注入しないようにする必要あります(すべてのクライアントで共有する必要はありません)。WebアプリケーションでSFSBを使用する場合は、JNDIルックアップを実行し、返されたEJBインスタンスをHttpSessionオブジェクトに格納して、今後のアクティビティに備える必要があります。そんな感じ:

try {
    InitialContext ctx = new InitialContext();
    myStateful = (MyStateful)ctx.lookup("java:comp/env/MyStatefulBean");
    session.setAttribute("my_stateful", myStateful);
} catch (Exception e) {
    // exception handling
}

片付けをありがとう。クライアントにスタンドアロンのコマンドラインプログラムを使用すると、違いがわかります。
スタンリーケリー

コメントをありがとう、彼らはより啓発的です。最初に抽象的な定義を与え、次に各状況のユースケースをいくつか指定し、次にいくつかの落とし穴を指摘します。素晴らしい+1
アーサー

EJB 3.1の場合も、回避注入部分はなくなりますか?
jacktrades

7
@Pascalの場合、「ステートフルセッションBean(SFSB)は1つのクライアント専用であり、それが一生に渡って使用されます」、つまり、この機能はSFSBに組み込まれているため、HttpSessionオブジェクトに保存する必要があるのはなぜですか。
user1169587

2
ステートフルBeanがすでに「セッション化」されているのに、セッションで保持する必要があるのはなぜですか?このようにして、すべてのオブジェクトをセッション化できます。
PLSを

18

このコンテキストでのステートレスおよびステートフルは、予想されるものとまったく同じではありません。

EJBのステートフル性とは、私が会話状態と呼ぶものを指します。典型的な例は、フライトの予約です。3つのステップで構成されている場合:

  • 予約席
  • クレジットカードに請求
  • チケットを発行

それぞれがセッションBeanへのメソッド呼び出しであると想像してください。ステートフルセッションBeanはこの種の会話を維持できるため、呼び出し間で発生することを記憶しています。

ステートレスセッションBeanには、会話状態に対応する能力はありません。

セッションBean(ステートレスまたはステートフル)内のグローバル変数は、まったく別のものです。ステートフルセッションBeanはBeanのプールを作成します(Beanは一度に1つの会話でのみ使用できるため)一方で、ステートレスセッションBeanは多くの場合1つのインスタンスしか持たないため、グローバル変数が機能しますが、私は思いませんこれは必ず保証されます。


5

2つの主要なタイプのセッションBean の主な違いは次のとおりです。

ステートレスBean

  1. ステートレスセッションBeanは、そのメソッドを呼び出したクライアントとの会話状態がないものです。このため、複数のクライアントとの対話に使用できるオブジェクトのプールを作成でき ます。
  2. クライアントごとの状態がないため、パフォーマンスの観点からステートレスBeanの優れています。
  3. 複数のクライアントからの複数のリクエストを並行して処理できます。

ステートフルビーンズ

  1. ステートフルセッションBeanは、一度に複数のクライアントとの会話状態を維持でき、タスクはクライアント間で共有されません。
  2. セッションが完了すると、状態保持されません
  3. コンテナーは、状態をシリアル化して、将来の使用のために古い状態として格納できます。これは、アプリケーションサーバーのリソースを節約し、Beanの障害をサポートするために行われます。

4

これは、コンテナがすべての呼び出しで再利用されるプール内のBeanインスタンスを1つだけ持っているために発生します。クライアントを並行して実行すると、コンテナーがプール内により多くのBeanインスタンスを作成するため、異なる結果が表示されます。


4

良い答えがあります。小さな答えを追加したいと思います。ステートレスBeanは、クライアントデータを保持するために使用しないでください。「ワンショットで実行できるアクションまたはプロセスをモデル化する」ために使用する必要があります。


4

良い質問、

このコードを試してください(MyBeanのステートフル/ステートレスを変更してください)。

import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.ejb.Stateless;

@LocalBean 
@Stateless 
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

Servlet_1

 import java.io.IOException;
    import javax.ejb.EJB;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.WebServlet;

    import java.io.PrintWriter;

    @WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
    public class ServletClient extends HttpServlet {

        private static final long serialVersionUID = 1L;

        @EJB
        MyBean mybean;

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

            PrintWriter out = response.getWriter();
            mybean.increment();
            out.println(mybean.getNumber());
        }

    }

Servlet_2

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

import java.io.PrintWriter;

@WebServlet(name = "NewServletClient", urlPatterns = { "/NewServletClient" })
public class NewServletClient extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

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

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

ケース:MyBean-@ Stateless

http:// localhost:8080 / MYServletDemo / ServletClient

1

http:// localhost:8080 / MYServletDemo / ServletClient

2

http:// localhost:8080 / MYServletDemo_war_exploded / newServletClient

http:// localhost:8080 / MYServletDemo / ServletClient

4

ケース:MyBean-@ Stateful

http:// localhost:8080 / MYServletDemo / ServletClient

1

http:// localhost:8080 / MYServletDemo / ServletClient

2

http:// localhost:8080 / MYServletDemo / newServletClient

1

http:// localhost:8080 / MYServletDemo / ServletClient


1
はい、それで終わりです。とても簡単な説明、ありがとう!
Nesquik27
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.