1. 简介

企业级会话Bean主要分为两大类:

  1. 无状态会话Bean
  2. 有状态会话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仓库 提供完整示例代码。


原始标题:Java EE Session Beans