问题
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.
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?
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.
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?
(edit) I renamed "class level" to "instance level" as this was causing some confusion.
回答1:
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.
回答2:
When I read What is a Session Bean? section of the J2EE 1.4 tutorial:
Stateless Session Beans
A stateless session bean 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.
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()
.
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!
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.)
As underlined by Rocket Surgeon, what do you mean exactly by class variable? Do you really mean class variable as 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 :)
回答3:
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.
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.
It would be much better to just pass those objects around between methods, and that would avoid all issues.
回答4:
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:
- 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 scope which could introduce memory leak problems where space in the Heap is used to keep objects that are not (or should be not) used anymore.
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.)
回答5:
Because this is very strange I performed a quick test with Netbeans and my local Glassfish 2.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.
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"; } }
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.
回答6:
It all hinges on what you mean by "class level variable". A class variable must have the static modifier. If clName
doesn't, then each instance of 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.
回答7:
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").
回答8:
The problem with using Instance variables in stateless Beans.
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.
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.
So it is not a good idea to use instance variables in stateless EJB beans .
回答9:
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.
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.
Hope help someone else :)
来源:https://stackoverflow.com/questions/1628009/stateless-session-bean-with-instance-variables