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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1628009/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:00:06  来源:igfitidea点击:

Stateless session bean with instance variables

javajakarta-eeglassfishstateless-session-bean

提问by Preston

I have a stateless session bean that contains one public method, several private methods, and some instance level variables. Below is a pseudo code example.

我有一个无状态会话 bean,它包含一个公共方法、几个私有方法和一些实例级变量。下面是一个伪代码示例。

private int instanceLevelVar

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

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

What I'm seeing is that methodB is printing values that weren't passed into MethodA. As best I can tell it's printing values from other instances of the same bean. What would cause this?

我看到的是 methodB 正在打印未传递到 MethodA 的值。尽我所能告诉它正在打印来自同一 bean 的其他实例的值。什么会导致这种情况?

I should point out the code works as expected 99.9% of the time. However, the .01% is causing some serious issues / concerns for me.

我应该指出代码在 99.9% 的情况下按预期工作。然而,0.01% 给我带来了一些严重的问题/担忧。

I understand that if I had different public methods then I might not get the same bean back between calls, which would result in this behavior. However, in this case the only call is to the single public method. Will the container (Glassfish in this case) still swap the beans out between private method calls?

我知道如果我有不同的公共方法,那么我可能无法在调用之间取回相同的 bean,这会导致这种行为。但是,在这种情况下,唯一的调用是对单个公共方法的调用。容器(在本例中为 Glassfish)是否仍会在私有方法调用之间交换 bean?

(edit) I renamed "class level" to "instance level" as this was causing some confusion.

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

采纳答案by Rocket Surgeon

I would just not bother using instance variable in stateless session bean at all. Regardless of what the cause of the issue you have encountered, it's probably not something you would want to do anyway. Just try using local variables throughout or define instance variables in helper classes you are calling from the stateless session bean business methods.

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

回答by fvu

Because this is very strange I performed a quick test with Netbeans and my local Glassfish 2.1.

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

  1. Create a new project using Samples->Java EE->Servlet Stateless. This creates an enterprise project with a simple stateless bean and a servlet that uses it.
  2. I modified the stateless bean to look like this, as close to your example as possible I think.

    @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";
       }
    }
    
  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";
       }
    }
    

This works as it should. I don't know what editor you're using, but if it's Netbeans it may be interesting to run it yourself.

这可以正常工作。我不知道您使用的是什么编辑器,但是如果是 Netbeans,那么自己运行它可能会很有趣。

回答by Pascal Thivent

When I read What is a Session Bean?section of the J2EE 1.4 tutorial:

当我阅读什么是会话 Bean?J2EE 1.4 教程的部分:

Stateless Session Beans

A stateless sessionbean does not maintain a conversational state for a particular client. When a client invokes the method of a stateless bean, the bean's instance variables may contain a state, but only for the duration of the invocation. When the method is finished, the state is no longer retained. Except during method invocation, all instances of a stateless bean are equivalent, allowing the EJB container to assign an instance to any client.

无状态会话 Bean

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

In your case, the call to methodB()from methodA()will be on the same instance and is equivalent to this.methodB(). I'm thus tend to say that methodB()can't output something else that the value that what was passed to methodA().

在您的情况下,对methodB()from的调用methodA()将在同一个实例上并且等效于this.methodB(). 因此,我倾向于说methodB()不能输出传递给methodA().

This is confirmed by the first sentence in section 7.11.8 in the EJB 2.0 spec: "The container must ensure that only one thread can be executing an instance at any time".This means you cannot come to a situation where data (in your instance variables) from different clients (threads) will be mixed. You are ensured unique access to the instance variables until methodA()has returned!

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

That said, I'm not saying that you don't have a problem somewhere. But I don't think that your pseudo code is equivalent.

也就是说,我并不是说你在某处没有问题。但我不认为你的伪代码是等效的。

(EDIT: Having read some comments to the OP's question, there is now clearly a doubt about the pseudo code and semantic used. I'm clarifying possible consequences below.)

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

As underlined by Rocket Surgeon, what do you mean exactly by class variable? Do you really mean class variableas opposed to instance variable? If yes, the pseudo code doesn't reflect it but this will clearly lead to unpredictable behavior. Actually, from section 24.1.2 (and first point) in the EJB 2.0 spec, it is clear that you are not allowed to write data to a class variable (although you can do it). There must be a good reason for this :)

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

回答by Jim Ferrans

It all hinges on what you mean by "class level variable". A class variable must have the static modifier. If clNamedoesn't, then each instanceof your stateless session bean has its own copy of clName. Your Java EE server probably created a pool of two or more instances of the stateless session bean, and each of your calls to testNa()and sayHello()gets sent to an arbitrary instance.

这一切都取决于您所说的“类级别变量”是什么意思。类变量必须具有静态修饰符。如果clName没有,那么无状态会话 bean 的每个实例都有自己的clName. 您的 Java EE 服务器可能创建了一个包含两个或多个无状态会话 bean 实例的池,并且您对任意实例的每次调用testNa()sayHello()发送都被发送到任意实例。

回答by Yishai

The likely cause of the issue is that the container is using the same object in two requests (therefore two threads) at the same time. So the first thread gets to line that calls methodB and then the next thread gets to the code which calls methodB and then the first thread executes the call to methodB, causing the issue. That would explain the behavior, at any rate. It doesn't seem to fit the spec, but that could just be a bug.

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

In general, even if permitted, keeping state in the bean is not a great idea. It leads to confusion code and can easily lead to bugs where you forget to start over with your all your state on every method call.

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

It would be much better to just pass those objects around between methods, and that would avoid all issues.

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

回答by Hank

I stumbled upon this question when I experienced the same problem. In my case, the private method actually sets the instance variable. What I have noticed is that sometimes the instance variable was already set, obviously from a previous request.

当我遇到同样的问题时,我偶然发现了这个问题。就我而言,私有方法实际上设置了实例变量。我注意到有时实例变量已经设置,显然是从以前的请求中设置的。

@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";
  }
}

I guess it makes sense. When the container re-uses a cached instance, how should it know how to clear the variables...

我想这是有道理的。当容器重用缓存的实例时,它应该如何知道如何清除变量...

To me, this is inline with and confirms both Pascal's reference to the EJB spec ("instance variables are supported") and Rocket Surgeon's recommendation ("don't do it, use local variables instead").

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

回答by puglieseweb

Probably your are not properly reinitializing the instance variable.

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

Instance variables

实例变量

In general we should not keep state in our stateless session bean. Objects referenced by instance variables, if not nulled after their use, are kept alive until the end of the request and even longer if our EJB container pools the session beans to reused. In the latter case we need to make sure that instance variable get properly reinitialized during a subsequent request. Therefore the use of instance variables may lead to the following issues:

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

  • during the same request, instance variable shared between different methods can easily lead to bugs where we forget to start over with the correct state on every method call
  • in case EJB container pools session beans and we may our code fails to properly reinitialize the instance variables we may reuse stale state set in a previous request
  • instance variables have instance scopewhich could introduce memory leak problems where space in the Heap is used to keep objects that are not (or should be not) used anymore.
  • 在同一个请求期间,不同方法之间共享的实例变量很容易导致错误,我们忘记在每次方法调用时以正确的状态重新开始
  • 如果 EJB 容器池会话 bean 并且我们可能我们的代码无法正确重新初始化实例变量,我们可能会重用先前请求中设置的陈旧状态
  • 实例变量具有实例范围,这可能会引入内存泄漏问题,其中堆中的空间用于保留不再(或不应)使用的对象。

Class variables

类变量

As for instance variables, class variables should not be used to keep shared state in Stateless session bean. This does not mean we should not use the static keyword but that we should use it with caution (e.g. define immutable constants, some static factory class, etc.)

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

回答by user2600752

The problem with using Instance variables in stateless Beans.

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

According to the JEE specification that same stateless EJB instance might be shared with another client as well. The thumb rule is not to create instance variables in Stateless EJBs.

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

It might be possible the two clients accessing the application simultaneously are provided same EJB instance which would create problems since there is data inconsistency.

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

So it is not a good idea to use instance variables in stateless EJB beans .

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

回答by Bemat

I had similar issue because I used global static class variable in my ejb class and when I had concurrent stateless EJB running, variable was overwritten by other instances.

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

Static class fields are shared among all instances of a particular class, but only within a single Java Virtual Machine (JVM). Updating a static class field implies an intent to share the field's value among all instances of the class.

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

Hope help someone else :)

希望能帮助别人:)