15

1つのパブリックメソッド、いくつかのプライベートメソッド、およびいくつかのインスタンスレベル変数を含むステートレスセッションBeanがあります。以下は擬似コードの例です。

private int instanceLevelVar

public void methodA(int x) { 
  this.instanceLevelVar = x;
  methodB();
}

private void methodB() {
  System.out.println(instanceLevelVar);
}

私が見ているのは、methodBがMethodAに渡されなかった値を出力しているということです。最善の方法として、同じBeanの他のインスタンスからの値を出力していることがわかります。これは何が原因でしょうか?

コードは99.9%の確率で期待どおりに機能することを指摘しておく必要があります。しかし、.01%は私にとっていくつかの深刻な問題/懸念を引き起こしています。

パブリックメソッドが異なると、呼び出し間で同じBeanが返されない可能性があり、その結果、この動作が発生する可能性があることを理解しています。ただし、この場合、唯一の呼び出しは単一のパブリックメソッドへの呼び出しです。コンテナ(この場合はGlassfish)は、プライベートメソッド呼び出し間でBeanを交換しますか?

(編集)混乱を引き起こしていたため、「クラスレベル」の名前を「インスタンスレベル」に変更しました。

4

9 に答える 9

25

「 SessionBeanとは」を読んだとき J2EE 1.4チュートリアルのセクション:

ステートレスセッションBean

ステートレスセッションBeanは、特定のクライアントの会話状態を維持しません。クライアントがステートレスBeanのメソッドを呼び出すと、Beanのインスタンス変数に状態が含まれる場合がありますが、呼び出しの期間中のみです。メソッドが終了すると、状態は保持されなくなります。メソッド呼び出し中を除いて、ステートレスBeanのすべてのインスタンスは同等であり、EJBコンテナがインスタンスを任意のクライアントに割り当てることができます。

methodB()あなたの場合、 fromへの呼び出しmethodA()は同じインスタンス上にあり、と同等this.methodB()です。したがって、私は、methodB()渡された値以外のものを出力できないと言う傾向がありますmethodA()

これは、 EJB 2.0仕様のセクション7.11.8の最初の文で確認されています。「コンテナは、常に1つのスレッドのみがインスタンスを実行できるようにする必要があります」。これは、異なるクライアント(スレッド)からのデータ(インスタンス変数内)が混在する状況に陥ることができないことを意味します。methodA()が戻るまで、インスタンス変数への一意のアクセスが保証されます。

とはいえ、どこかで問題がないと言っているのではありません。しかし、私はあなたの擬似コードが同等であるとは思いません。

(編集:OPの質問に対するいくつかのコメントを読んだので、使用されている擬似コードとセマンティクスについて明らかに疑問があります。以下で考えられる結果を明確にしています。)

Rocket Surgeonが強調しているように、クラス変数とは正確にはどういう意味ですか?インスタンス変数ではなく、クラス変数を本当に意味しますか?はいの場合、擬似コードはそれを反映しませんが、これは明らかに予測できない動作につながります。実際、EJB 2.0仕様のセクション24.1.2(および最初のポイント)から、クラス変数へのデータの書き込みが許可されていないことは明らかです(ただし、それは可能です)。これには正当な理由があるはずです:)

于 2009-10-27T00:56:52.533 に答える
10

ステートレスセッションBeanでインスタンス変数を使用することはまったくありません。発生した問題の原因に関係なく、とにかくやりたいことではないでしょう。全体でローカル変数を使用するか、ステートレスセッションBeanのビジネスメソッドから呼び出すヘルパークラスでインスタンス変数を定義してみてください。

于 2009-10-27T17:08:54.750 に答える
5

この問題の考えられる原因は、コンテナが2つのリクエスト(したがって2つのスレッド)で同時に同じオブジェクトを使用していることです。したがって、最初のスレッドはmethodBを呼び出す行に到達し、次のスレッドはmethodBを呼び出すコードに到達し、最初のスレッドはmethodBへの呼び出しを実行して問題を引き起こします。とにかく、それは振る舞いを説明するでしょう。仕様に適合していないようですが、それはバグである可能性があります。

一般に、許可されている場合でも、Beanの状態を維持することは良い考えではありません。それは混乱のコードにつながり、すべてのメソッド呼び出しですべての状態を最初からやり直すのを忘れるバグに簡単につながる可能性があります。

これらのオブジェクトをメソッド間で渡すだけの方がはるかに優れており、すべての問題を回避できます。

于 2009-10-27T17:19:17.800 に答える
3

おそらく、インスタンス変数を適切に再初期化していない可能性があります。

インスタンス変数

一般に、ステートレスセッションBeanで状態を維持するべきではありません。インスタンス変数によって参照されるオブジェクトは、使用後にnullにされない場合、リクエストが終了するまで存続し、EJBコンテナがセッションBeanをプールして再利用する場合はさらに長くなります。後者の場合、後続のリクエスト中にインスタンス変数が適切に再初期化されることを確認する必要があります。したがって、インスタンス変数を使用すると、次の問題が発生する可能性があります。

  • 同じリクエスト中に、異なるメソッド間で共有されるインスタンス変数は、すべてのメソッド呼び出しで正しい状態からやり直すのを忘れるバグに簡単につながる可能性があります
  • EJBコンテナがセッションBeanをプールし、コードがインスタンス変数を適切に再初期化できない場合は、前のリクエストで設定された古い状態を再利用できます。
  • インスタンス変数にはインスタンススコープがあり、ヒープ内のスペースを使用して、使用されなくなった(または使用されるべきではない)オブジェクトを保持するというメモリリークの問題が発生する可能性があります。

クラス変数

インスタンス変数の場合、ステートレスセッションBeanで共有状態を維持するためにクラス変数を使用しないでください。これは、staticキーワードを使用すべきではないという意味ではありませんが、注意して使用する必要があることを意味します(たとえば、不変の定数、静的ファクトリクラスの定義など)。

于 2014-01-05T20:37:17.433 に答える
2

これは非常に奇妙なことなので、NetbeansとローカルのGlassfish2.1を使用して簡単なテストを実行しました。

  1. 「サンプル」->「JavaEE」->「サーブレットステートレス」を使用して新しいプロジェクトを作成します。これにより、単純なステートレスBeanとそれを使用するサーブレットを使用してエンタープライズプロジェクトが作成されます。
  2. ステートレスBeanをこのように変更しました。これは、可能な限りあなたの例に近いと思います。

    @Stateless
    public class StatelessSessionBean implements StatelessSession {
    
       String clName;
    
       private void testNa() {
          System.out.println(clName);
       }
    
       public String sayHello(String name) {
          this.clName = name;
          testNa();
          return "Testcase";
       }
    }
    

これは正常に機能します。使用しているエディタはわかりませんが、Netbeansの場合は、自分で実行するのが面白いかもしれません。

于 2009-10-27T00:54:46.500 に答える
0

それはすべて、「クラスレベル変数」の意味にかかっています。クラス変数には静的修飾子が必要です。そうclNameでない場合は、ステートレスセッションBeanの各インスタンスに独自のコピーがありclNameます。Java EEサーバーは、おそらくステートレスセッションBeanの2つ以上のインスタンスのプールを作成し、各インスタンスへの呼び出しとtestNa()任意sayHello()のインスタンスへの送信を行います。

于 2009-10-27T02:23:14.870 に答える
0

同じ問題を経験したとき、私はこの質問に出くわしました。私の場合、privateメソッドは実際にインスタンス変数を設定します。私が気付いたのは、明らかに以前のリクエストから、インスタンス変数がすでに設定されている場合があるということです。

@Stateless
public class MyBean {
  String someString;

  public void doSomething() {
    internalDoSomething();
  }

  private void internalDoSomething() {
    if (someString != null) {
      System.out.println("oops, someString already contained old data");
    }

    this.someString = "foo";
  }
}

私はそれが理にかなっていると思います。コンテナがキャッシュされたインスタンスを再利用する場合、変数をクリアする方法をどのように知る必要がありますか...

私にとって、これは、EJB仕様へのPascalの参照(「インスタンス変数がサポートされている」)とRocket Surgeonの推奨(「代わりにローカル変数を使用する」)の両方と一致しており、確認しています。

于 2011-08-24T09:01:42.050 に答える
0

ステートレスBeanでインスタンス変数を使用する際の問題。

JEE仕様によると、同じステートレスEJBインスタンスが別のクライアントとも共有される可能性があります。サムルールは、ステートレスEJBでインスタンス変数を作成しないことです。

アプリケーションに同時にアクセスする2つのクライアントに同じEJBインスタンスが提供される可能性があります。これにより、データの不整合が発生するため、問題が発生します。

したがって、ステートレスEJBBeanでインスタンス変数を使用することはお勧めできません。

于 2016-09-23T14:31:52.543 に答える
0

ejbクラスでグローバル静的クラス変数を使用し、ステートレスEJBを同時に実行していると、変数が他のインスタンスによって上書きされたため、同様の問題が発生しました。

静的クラスフィールドは、特定のクラスのすべてのインスタンス間で共有されますが、単一のJava仮想マシン(JVM)内でのみ共有されます。静的クラスフィールドを更新することは、クラスのすべてのインスタンス間でフィールドの値を共有する意図を意味します。

他の誰かを助けてください:)

于 2017-05-15T10:12:39.833 に答える