无状态Bean注入有状态Bean后JBoss升级引发的数据污染问题咨询
Let's break down exactly what's happening here and how to fix it—this is a classic stateless EJB statefulness bug that got exposed by the JBoss upgrade.
The Root Cause
- Stateless EJBs are poolable, not per-request/per-session: When you mark a bean with
@Stateless, the JBoss EJB container maintains a pool of instances to serve incoming requests. Multiple requests (even from different users/sessions) can reuse the same EJB instance. Any instance-level variables (like yourListmember) are shared across all requests that use that instance. - JBoss 5.1 vs 7.0 behavior differences: JBoss 5.1 (Java EE 5) had a less aggressive instance pooling strategy compared to JBoss 7 (Java EE 6). In your old setup, you might have gotten lucky—requests rarely reused the same EJB instance, so the shared
Listdidn't cause visible issues. JBoss 7 optimizes instance reuse to improve performance, which made this hidden bug surface. - JSF stateful beans don't isolate EJB instances: Your stateful JSF backing bean (per-session, presumably) gets injected with a stateless EJB instance from the pool. If another session's backing bean gets the same EJB instance, their data will mix in that shared
List.
Fixes to Resolve the Cross-Request Data Leak
1. Move the List to a method-local variable (best practice for stateless EJBs)
Stateless EJBs should never hold instance-level state. Rewrite your EJB to create the List inside the method that uses it, so each method call gets its own isolated List:
Bad (original code):
@Stateless public class MyServiceBean { private List<String> dataList = new ArrayList<>(); public void addData(String data) { dataList.add(data); // ... other logic } // ... other methods using dataList }
Good (fixed code):
@Stateless public class MyServiceBean { public void addData(String data) { List<String> dataList = new ArrayList<>(); // Local to the method dataList.add(data); // ... other logic } // If you need to pass the list between methods in the same call, pass it as a parameter public void processList(List<String> dataList) { // ... process the list } }
2. Switch to a @Stateful EJB (if you need to maintain state across method calls)
If your business logic requires maintaining state across multiple method calls from the same user/session, change the EJB to @Stateful. Stateful EJBs are tied to a single client session, so their instance variables won't be shared across users:
@Stateful public class MyServiceBean { private List<String> dataList = new ArrayList<>(); public void addData(String data) { dataList.add(data); } // Don't forget to implement a remove method to clean up resources @Remove public void cleanup() { dataList.clear(); } }
Note: When using stateful EJBs with Seam/JSF, make sure to bind the EJB to the same scope as your backing bean (e.g., session scope) and call the
@Removemethod when the session ends to avoid resource leaks.
3. Move the state to the JSF backing bean
Since your JSF backing bean is already stateful, you can hold the List there instead of in the EJB. The EJB then acts as a stateless service that operates on the list passed to it:
// Stateful JSF backing bean @Name("myBackingBean") @Scope(ScopeType.SESSION) public class MyBackingBean { private List<String> dataList = new ArrayList<>(); @Inject private MyServiceBean serviceBean; public void addData(String data) { dataList.add(data); serviceBean.processData(dataList); // Pass the list to the stateless EJB } } // Stateless EJB (no instance state) @Stateless public class MyServiceBean { public void processData(List<String> dataList) { // ... process the list without storing it } }
Key Takeaway
Stateless EJBs are designed to be stateless—never store request-specific or session-specific data in instance variables. JBoss 7's improved instance pooling just made a problem that was already present more obvious. Stick to method-local variables or switch to stateful EJBs/JSF backing bean state when you need to maintain data across calls.
内容的提问来源于stack exchange,提问作者Luiz Guilherme Rodrigues

