具有实例变量的无状态会话bean

我有一个无状态会话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?

(编辑)我将“类级别”重命名为“实例级别”,因为这引起了一些混乱。

我根本不会在无状态会话bean中使用实例变量。 无论您遇到的问题的原因是什么,它可能都不是您想要做的事情。 只需尝试使用局部变量,或者在无状态会话bean业务方法中调用的辅助类中定义实例变量。

当我读什么是会话Bean? J2EE 1.4教程的一节:

无状态会话豆

无状态会话 Bean不维护特定客户端的会话状态。 当客户端调用无状态bean的方法时,bean的实例变量可能包含一个状态,但仅限于调用期间。 方法完成后,不再保留状态。 除了在方法调用期间,无状态bean的所有实例都是等效的,允许EJB容器将实例分配给任何客户端。

在您的情况下,从methodB()调用methodB()将在同一个实例上,并且等同于this.methodB() 。 因此,我倾向于说methodB()不能输出传递给methodB()的值的其他内容。

EJB 2.0规范中第7.11.8节的第一句证实了这一点: “容器必须确保任何时候只有一个线程可以执行实例”。 这意味着您不会遇到来自不同客户端(线程)的数据(在您的实例变量中)将被混合的情况。 在methodA()返回之前,您可以确保对实例变量进行唯一访问!

那就是说,我不是说你在某个地方没有问题。 但我不认为你的伪代码是等价的。

(编辑:读过一些关于OP问题的评论,现在显然对使用的伪代码和语义有疑问。我在下面澄清可能的后果。)

正如火箭外科医生所强调的那样,你的意思是什么? 你真的是指类变量而不是实例变量吗? 如果是,则伪代码不反映它,但这显然会导致不可预测的行为。 实际上,从EJB 2.0规范的第24.1.2节(和第一点)开始,很明显你不允许将数据写入类变量(尽管你可以这样做)。 必须有一个很好的理由:)

问题的可能原因是容器在两个请求中同时使用相同的对象(因此是两个线程)。 所以第一个线程到达调用methodB的行,然后下一个线程到达调用methodB的代码,然后第一个线程执行对methodB的调用,从而导致问题。 这无论如何都可以解释这种行为。 它似乎不符合规范,但这可能只是一个错误。

一般来说,即使允许,保持bean中的状态也不是一个好主意。 它导致混淆代码,并且很容易导致错误,在每次方法调用时忘记重新启动所有状态。

在方法之间传递这些对象会好得多,这样可以避免所有问题。

可能你没有正确地重新初始化实例变量。

实例变量

一般来说,我们不应该在无状态会话bean中保持状态。 实例变量引用的对象,如果在使用后未被清零,则保持活动直到请求结束,如果我们的EJB容器将会话bean汇集到重用中,则更长。 在后一种情况下,我们需要确保在后续请求期间正确地重新初始化实例变量。 因此,使用实例变量可能会导致以下问题:

  • 在同一个请求中,不同方法之间共享的实例变量很容易导致错误,我们忘记在每个方法调用时重新启动正确的状态
  • 如果EJB容器池会话bean,我们的代码可能无法正确重新初始化实例变量,我们可能会重用先前请求中设置的陈旧状态
  • 实例变量具有可能引入内存泄漏问题的实例范围 ,其中Heap中的空间用于保留不再(或不应该)使用的对象。

类变量

对于实例变量,不应使用类变量来保持无状态会话bean中的共享状态。 这并不意味着我们不应该使用static关键字,而是应该谨慎使用它(例如,定义不可变常量,一些静态工厂类等)

因为这很奇怪,我用Netbeans和我当地的Glassfish 2.1进行了快速测试。

  1. 使用Samples-> Java EE-> Servlet Stateless创建一个新项目。 这将创建一个企业项目,其中包含一个简单的无状态bean和一个使用它的servlet。
  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,那么自己运行它可能会很有趣。

这完全取决于你所谓的“class级变量”。 类变量必须具有static修饰符。 如果clName没有,那么无状态会话bean的每个实例都有自己的clName副本。 您的Java EE服务器可能创建了一个由两个或多个无状态会话bean实例组成的池,并且您对testNa()sayHello()每个调用都会被发送到任意实例。

当我遇到同样的问题时,我偶然发现了这个问题。 在我的例子中,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"; } } 

我想这很有道理。 当容器重新使用缓存实例时,它应该如何清除变量…

对我来说,这是内联并确认了Pascal对EJB规范的引用(“支持实例变量”)和Rocket Surgeon的建议(“不要这样做,而是使用局部变量”)。

在无状态Bean中使用实例变量的问题。

根据JEE规范,同样的无状态EJB实例也可能与另一个客户端共享。 拇指规则不是在无状态EJB中创建实例变量。

有可能同时访问应用程序的两个客户端提供相同的EJB实例,这会产生问题,因为存在数据不一致。

因此,在无状态EJB bean中使用实例变量并不是一个好主意。

我有类似的问题,因为我在我的ejb类中使用了全局静态类变量,当我运行并发无状态EJB时,变量被其他实例覆盖。

静态类字段在特定类的所有实例之间共享,但仅在单个Java虚拟机(JVM)中共享。 更新静态类字段意味着在类的所有实例之间共享字段值的意图。

希望能帮助别人:)