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タイプの例を教えてください。ステートフルセッションBeanをステートフルに変更すると動作が異なりますか?
重要な違いは、プライベートメンバー変数ではなく、状態を特定のユーザーに関連付けることです(「ショッピングカート」を考えてください)。
ステートフルセッションBeanのステートフルな部分は、サーブレットのセッションに似ています。ステートフルセッションBeanを使用すると、Webクライアントがなくてもアプリでそのセッションを保持できます。アプリサーバーは、オブジェクトプールからステートレスセッションBeanを取得すると、特定のユーザーに関連付けられていないため、任意の要求を満たすために使用できることを認識しています。
ステートフルセッションBeanは、ショッピングカート情報は自分だけが知っている必要があるため、最初に取得したユーザーに渡さなければなりません。アプリサーバーは、これが正しいことを確認します。ショッピングを開始でき、アプリサーバーがあなたにステートフルセッションBeanを渡してくれた場合、アプリがどれほど人気になるか想像してみてください!
したがって、プライベートデータメンバーは確かに「状態」ですが、「ショッピングカート」ではありません。増分された変数が特定のユーザーに関連付けられるように、(非常に良い)例を再試行してください。増分し、新しいユーザーを作成し、増分された値が引き続き表示されるかどうかを確認します。正しく行われた場合、すべてのユーザーはカウンターのバージョンだけを見る必要があります。
ステートレスセッションBean(SLSB)は結び付けられていない 1つのクライアントにあり、保証なし各メソッド呼び出しで同じインスタンスを取得する1つのクライアント(一部のコンテナーは作成および破棄される場合があります)各メソッド呼び出しセッションでのBean、これは実装固有の決定ですが、インスタンスは通常プールされます-クラスター環境については言及しません)。つまり、ステートレスBeanにはインスタンス変数がありますが、これらのフィールドは1つのクライアントに固有のものではないため、リモートコール間でそれらに依存しないでください。
対照的に、ステートフルセッションBean(SFSB)は、生涯を通じて1つのクライアントに対して専用であり、インスタンスのスワッピングやプーリングはありません(リソースを節約するためにパッシベーション後にメモリから削除される場合がありますが、それは別の話です)および会話状態の維持。つまり、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
}
このコンテキストでのステートレスとステートフルは、あなたが期待するものをまったく意味しません。
EJBのステートフルネスとは、私が会話状態と呼ぶものを指します。典型的な例はフライト予約です。 3つのステップで構成される場合:
これらのそれぞれがセッションBeanのメソッド呼び出しであると想像してください。ステートフルセッションBeanは、この種の会話を維持できるため、呼び出し間で何が起こるかを記憶します。
ステートレスセッションBeanには、会話状態に対するそのような能力はありません。
セッションBean内のグローバル変数(ステートレスまたはステートフル)はまったく別のものです。ステートフルセッションBeanにはBeanのプールが作成されます(Beanは一度に1つの会話でしか使用できないため)これは必ず保証されます。
これは、すべての呼び出しで再利用されているプール内のコンテナが1つだけであるために発生します。クライアントを並行して実行すると、コンテナがプールにさらに多くのBeanインスタンスを作成するため、異なる結果が表示されます。
セッションBeanの2つの主要なタイプの主な違いは次のとおりです。
ステートレスBean
ステートフルBean
良い答えがあります。私は小さな答えを追加したいと思います。ステートレスBeanは、クライアントデータを保持するために使用しないでください。 「ワンショットで実行できるアクションまたはプロセスをモデル化する」ために使用する必要があります。
良い質問、
このコードを試してください(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());
}
}
case:MyBean-@ Stateless
http:// localhost:8080/MYServletDemo /ServletClient
1
http:// localhost:8080/MYServletDemo /ServletClient
2
http:// localhost:8080/MYServletDemo_war_exploded /newServletClient
3
http:// localhost:8080/MYServletDemo /ServletClient
4
case: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
3