1. 简介
企业级会话Bean主要分为两大类:
- 无状态会话Bean
- 有状态会话Bean
本文将深入剖析这两种核心会话Bean的工作原理和使用场景。
2. 环境准备
要使用Enterprise Beans 3.2,请在pom.xml中添加最新依赖:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
3. 无状态Bean
无状态会话Bean常用于执行独立操作,它不维护客户端状态,但可保留实例状态。⚠️ 这里有个常见误区:实例状态≠客户端状态!
3.1 创建无状态Bean
首先用@Stateless
注解标记Bean:
@Stateless
public class StatelessEJB {
public String name;
}
创建第一个客户端EJBClient1
:
public class EJBClient1 {
@EJB
public StatelessEJB statelessEJB;
}
再创建第二个客户端EJBClient2
:
public class EJBClient2 {
@EJB
public StatelessEJB statelessEJB;
}
3.2 测试无状态特性
通过以下测试验证状态独立性:
@RunWith(Arquillian.class)
public class StatelessEJBTest {
@Inject
private EJBClient1 ejbClient1;
@Inject
private EJBClient2 ejbClient2;
@Test
public void givenOneStatelessBean_whenStateIsSetInOneBean
_secondBeanShouldHaveSameState() {
ejbClient1.statelessEJB.name = "Client 1";
assertEquals("Client 1", ejbClient1.statelessEJB.name);
assertEquals("Client 1", ejbClient2.statelessEJB.name); // 关键验证点
}
@Test
public void givenOneStatelessBean_whenStateIsSetInBothBeans
_secondBeanShouldHaveSecondBeanState() {
ejbClient1.statelessEJB.name = "Client 1";
ejbClient2.statelessEJB.name = "Client 2";
assertEquals("Client 2", ejbClient2.statelessEJB.name); // 后操作覆盖
}
}
核心验证点:
- ✅ Client1设置状态后,Client2立即看到相同值(共享实例)
- ✅ Client2设置状态后,覆盖原有值(无状态特性)
踩坑提醒:新手常误以为
name
字段属于特定客户端,实际容器可能复用同一实例处理多个请求!
4. 有状态Bean
有状态会话Bean在事务内外都能维持状态,每个Bean实例与特定客户端绑定。容器会自动管理Bean的状态序列化(钝化/激活)。
4.1 创建有状态Bean
使用@Stateful
注解标记:
@Stateful
public class StatefulEJB {
public String name;
}
创建客户端EJBClient1
:
public class EJBClient1 {
@EJB
public StatefulEJB statefulEJB;
}
创建客户端EJBClient2
:
public class EJBClient2 {
@EJB
public StatefulEJB statefulEJB;
}
4.2 测试状态保持
验证客户端状态隔离性:
@RunWith(Arquillian.class)
public class StatefulEJBTest {
@Inject
private EJBClient1 ejbClient1;
@Inject
private EJBClient2 ejbClient2;
@Test
public void givenOneStatefulBean_whenTwoClientsSetValueOnBean
_thenClientStateIsMaintained() {
ejbClient1.statefulEJB.name = "Client 1";
ejbClient2.statefulEJB.name = "Client 2";
assertNotEquals(ejbClient1.statefulEJB.name, ejbClient2.statefulEJB.name); // 状态隔离
assertEquals("Client 1", ejbClient1.statefulEJB.name); // 各自保持
assertEquals("Client 2", ejbClient2.statefulEJB.name);
}
}
关键验证点:
- ✅ 两个客户端设置不同值后互不影响
- ✅ 每个客户端维护独立状态
简单粗暴理解:有状态Bean就像为每个客户端分配专属"储物柜",无状态Bean则是公共储物柜(谁用谁清空)。
5. 无状态 vs 有状态会话Bean
5.1 无状态Bean特性
- 状态管理:不维护客户端状态,适合对象池化
- 性能优势:无客户端状态负担,吞吐量更高
- 并发处理:可并行处理多客户端请求
- 典型场景:数据库查询、通用计算服务
5.2 有状态Bean特性
- 状态管理:维护客户端专属状态,状态不共享
- 生命周期:状态随会话存在,会话销毁则状态清除
- 钝化机制:容器可序列化状态以释放资源(Passivation)
- 典型场景:购物车、多步骤工作流、生产者-消费者问题
6. 总结
我们通过实际代码演示了两种会话Bean的核心差异:
- ✅ 无状态Bean:高性能、共享实例、无客户端状态
- ✅ 有状态Bean:状态隔离、会话绑定、支持复杂业务流程
源码参考:GitHub仓库 提供完整示例代码。