インスタンス変数を使用したステートレスセッションBean
-
06-07-2019 - |
質問
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をスワップしますか?
(編集)" class level"という名前に変更しました「インスタンスレベル」へこれにより混乱が生じていたためです。
解決
ステートレスセッションBeanでインスタンス変数をまったく使用しません。あなたが遭遇した問題の原因に関係なく、それはおそらくあなたがとにかくやりたいことではないでしょう。全体でローカル変数を使用するか、ステートレスセッションBeanビジネスメソッドから呼び出すヘルパークラスでインスタンス変数を定義するだけです。
他のヒント
セッションBeanとは何ですか? J2EE 1.4チュートリアルのセクション:
ステートレスセッションBean
ステートレスセッション Beanは、特定のクライアントの会話状態を維持しません。クライアントがステートレスBeanのメソッドを呼び出すと、Beanのインスタンス変数に状態が含まれる場合がありますが、呼び出しの期間のみです。メソッドが終了すると、状態は保持されなくなります。メソッドの呼び出し中を除き、ステートレスBeanのすべてのインスタンスは同等であり、EJBコンテナはインスタンスを任意のクライアントに割り当てることができます。
あなたの場合、 methodA()
からの methodB()
への呼び出しは同じインスタンス上にあり、 this.methodB()<と同等です/ code>。したがって、
methodB()
は methodA()
に渡された値以外の何かを出力できないと言う傾向があります。
これは、 EJB 2.0仕様:&quot;コンテナは、常に1つのスレッドのみがインスタンスを実行できるようにする必要があります。 これは、異なるクライアント(スレッド)からのデータ(インスタンス変数内)が混在する状況にならないことを意味します。 methodA()
が返されるまで、インスタンス変数への一意のアクセスが保証されます!
それは、あなたがどこかに問題がないと言っているわけではありません。しかし、あなたの擬似コードは同等だとは思いません。
(編集:OPの質問に対するいくつかのコメントを読んだので、使用されている擬似コードとセマンティックスについて明らかに疑念があります。以下に考えられる結果を明確にします。)
Rocket Surgeonが強調したように、クラス変数とはどういう意味ですか? インスタンス変数ではなく、クラス変数を本当に意味しますか?はいの場合、擬似コードはそれを反映しませんが、これは明らかに予測不可能な動作につながります。実際、EJB 2.0仕様のセクション24.1.2(および最初のポイント)から、クラス変数へのデータの書き込みが許可されていないことは明らかです(できますが)。これには十分な理由があるはずです:)
この問題の原因として考えられるのは、コンテナが2つのリクエスト(したがって2つのスレッド)で同じオブジェクトを同時に使用していることです。したがって、最初のスレッドはmethodBを呼び出す行に到達し、次のスレッドはmethodBを呼び出すコードに到達してから、最初のスレッドがmethodBの呼び出しを実行して、問題が発生します。とにかく、それは動作を説明するでしょう。仕様に合わないようですが、それは単なるバグかもしれません。
一般に、たとえ許可されていても、Beanの状態を維持することは素晴らしい考えではありません。混乱のコードにつながり、すべてのメソッド呼び出しですべての状態からやり直すことを忘れるバグに簡単につながる可能性があります。
これらのオブジェクトをメソッド間で渡すだけで、すべての問題を回避できます。
おそらく、インスタンス変数を適切に再初期化していないでしょう。
インスタンス変数
一般に、ステートレスセッションBeanで状態を保持するべきではありません。インスタンス変数によって参照されるオブジェクトは、使用後にnullにならなかった場合、リクエストの最後まで、そしてEJBコンテナが再利用するセッションBeanをプールする場合はさらに長く生き続けます。後者の場合、後続のリクエスト中にインスタンス変数が適切に再初期化されるようにする必要があります。したがって、インスタンス変数を使用すると、次の問題が発生する可能性があります。
- 同じリクエスト中に、異なるメソッド間で共有されているインスタンス変数は、すべてのメソッド呼び出しで正しい状態からやり直すことを忘れるというバグを容易に引き起こす可能性があります
- EJBコンテナがセッションBeanをプールし、コードがインスタンス変数を適切に再初期化できない場合、以前のリクエストで設定された古い状態を再利用する
- インスタンス変数には、インスタンススコープがあります。これにより、ヒープ内のスペースが使用されなくなった(または使用すべきでない)オブジェクトを保持するためにメモリリークの問題が発生する可能性があります。
クラス変数
インスタンス変数については、ステートレスセッションBeanで共有状態を維持するためにクラス変数を使用しないでください。これは、静的キーワードを使用しないことを意味しませんが、注意して使用する必要があります(たとえば、不変の定数、静的ファクトリクラスなどを定義します)
これは非常に奇妙なので、NetbeansとローカルのGlassfish 2.1で簡単なテストを行いました。
- Samples-&gt; Java EE-&gt; Servlet Statelessを使用して新しいプロジェクトを作成します。これにより、シンプルなステートレスBeanとそれを使用するサーブレットでエンタープライズプロジェクトが作成されます。
-
可能な限りあなたの例に近いように、ステートレス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の場合は自分で実行するのが面白いかもしれません。
すべては、「クラスレベル変数」の意味に依存します。クラス変数にはstatic修飾子が必要です。 clName
にない場合、ステートレスセッションBeanの各インスタンスには、 clName
の独自のコピーがあります。 Java EEサーバーは、おそらくステートレスセッションBeanの2つ以上のインスタンスのプールを作成し、 testNa()
および sayHello()
への呼び出しはそれぞれ、任意のインスタンス。
同じ問題に遭遇したとき、私はこの質問につまずいた。私の場合、プライベートメソッドは実際にインスタンス変数を設定します。私が気づいたのは、明らかに以前のリクエストから明らかにインスタンス変数がすでに設定されていることです。
@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";
}
}
それは理にかなっていると思います。コンテナがキャッシュされたインスタンスを再利用する場合、変数をクリアする方法をどのように知る必要がありますか...
これは、PascalのEJB仕様への参照(「インスタンス変数はサポートされています」)とRocket Surgeonの推奨事項(「やらないで、代わりにローカル変数を使用」)の両方をインラインで確認します。
p>ステートレスBeanでのインスタンス変数の使用に関する問題。
JEE仕様によると、同じステートレスEJBインスタンスも別のクライアントと共有される可能性があります。経験則では、ステートレスEJBでインスタンス変数を作成しません。
アプリケーションに同時にアクセスする2つのクライアントに同じEJBインスタンスが提供される可能性がありますが、データの不整合があるため問題が発生します。
したがって、ステートレスEJB Beanでインスタンス変数を使用することはお勧めできません。
ejbクラスでグローバルな静的クラス変数を使用し、同時にステートレスEJBを実行していると、変数が他のインスタンスによって上書きされたため、同様の問題が発生しました。
静的クラスフィールドは、特定のクラスのすべてのインスタンス間で共有されますが、単一のJava仮想マシン(JVM)内でのみ共有されます。静的クラスフィールドの更新は、クラスのすべてのインスタンス間でフィールドの値を共有する意図を意味します。
他の誰かを助けてください:)