ここでは、Stateful Bean を使ってみます。
以下にコードを載せます。
Sample.java
import javax.ejb.Remote; @Remote public interface Sample { void add(int count); void show(); }
SampleBean.java
import javax.ejb.Stateful; @Stateful public class SampleBean implements Sample { private int total = 0; public void add(int count) { System.out.println("Add " + count); total += count; } public void show() { System.out.println("Total = " + total); } }
Stateless Bean とほとんど同じです。
違うのは @Stateful というAnnotationを使っていること位です。
そして、「Stateful」というくらいですから
このBeanは状態を持つことが出来ます。
具体的に言えば、フィールドを持つことが出来るという事です。
private int total = 0;
Stateless Bean では、EJB実装クラスにフィールドを定義することは出来ません。
正確に言えば、定義して使うことは可能ですが
フィールドの値が保持されることが「保証されません」。
EJBサーバは、状況に応じてStateless Beanを破棄したり生成したりします。
Stateful Beanは、クライアント側で指示しない限り破棄されることは無いので
フィールドの内容は保持されることが保証されます。
これだけ聞くと、Stateless Beanを使うメリットなど無いように思えますが
そんな事はありません。Stateless BeanはStateful Beanに比べて
圧倒的なパフォーマンスを誇ります。
EJBはただでさえパフォーマンスに難点があり、
出来るだけStateless Beanを使うことが推奨されていることは覚えておきましょう。
public class Client { public static void main(String[] args) throws Exception { Context ctx = new InitialContext(); Sample ejb = (Sample) ctx.lookup(Sample.class.getName()); ejb.add(100); ejb.add(200); ejb.show(); } }
出力結果は以下のようになります。
Add 100 Add 200 Total = 300
Stateful Beanなので、最初のaddメソッド呼び出しから最後のshowメソッド呼び出しまで
ejbインスタンス内のcountフィールドは保持されることが保証されています。
一見、「そんなの当たり前じゃん」と思う方もいるかもしれません。
しかし、仮にこのBeanをStateless Beanで定義していた場合、このようにはなりません。
この場合、最初のaddメソッド呼び出し時のejbインスタンスと
2回名のaddメソッド呼び出し時のejbインスタンスは
同一とは限らないのです。
「そんな事あるか」って?これがあるんです。
countフィールドはejbインスタンスと直接結び付いておらず、
リモート呼び出しによる参照に過ぎません。
つまり、countフィールドの実体はClient側ではなくJBoss側に存在しています。
JBossはejb(Sampleインターフェイス)からのメソッド呼び出しがあると
JBoss側のBean(SampleBeanインスタンス)にアクセスしますが、
このBeanは必要に応じて破棄される可能性があります。
それに比べて、Stateful BeanはEJBサーバによって管理され
明示的な呼び出しが無い限りJBossはこのインスタンスを保持し続けます。
この管理の手間が、Stateful Beanの性能がStatelessよりも劣っている原因です。
仮に、上の例をStateless Beanで作ってみたらどうなるでしょうか?
例えば、以下のような結果になる可能性があります。
Add 100 Add 200 Total = 200
2回目のaddメソッド呼び出し時に、SampleBeanインスタンスが新規生成されたので
このときフィールドcountは0になってしまいました。
よって、add(200) の呼び出しによってcountは200になります。
実際には、このような単純な例で
SampleBeanインスタンスが破棄される可能性はまず無いのですが、
もしこのロジックが数分にも渡る長い処理だったり
他にたくさんのロジックがSampleBeanインスタンスを使っていた場合には
こういった事が起こる可能性があるのです。
プログラムの世界では、「失敗する可能性がある」という事は
「必ず失敗する」という事と同じ程度の認識が必要です。
つまり、こういったロジックを組んではいけません。