无状态会话 Bean 与 Singleton 会话 Bean

2022-09-01 12:16:37

Java EE 6教程说:

为了提高性能,您可以选择无状态会话 Bean,如果它具有以下任何特征:

  • Bean 的状态没有特定客户端的数据。
  • 在单个方法调用中,Bean 为所有客户机执行一个通用任务。例如,您可以使用无状态会话 Bean 发送确认在线订单的电子邮件。
  • 该 Bean 实现了一个 Web 服务。

单例会话 Bean 适用于以下情况:

  • 状态需要在整个应用程序中共享。
  • 单个企业 Bean 需要由多个线程同时访问。
  • 应用程序需要一个企业 Bean 在应用程序启动和关闭时执行任务。
  • 该 Bean 实现了一个 Web 服务。

但是,如果出现以下情况,该怎么办:

  • 无需在应用程序之间共享任何状态
  • 单个企业 Bean 可由多个线程同时访问
  • 启动或击落时无需执行任何任务

例如,假设我有一个具有以下界面的登录服务:

public interface LoginService {
  boolean authenticate(String user, String password);
}

应该用@Singleton还是@Stateless注释?这两者有什么好处?如果LoginService需要注入一个EntityManager(将同时使用)怎么办?

加法:我正在考虑Spring服务bean的Java EE对应物,它们是无状态的单例。如果我正确地理解 Java EE 对应项是@Stateless会话 bean 和 @Singleton Bean 用于在启动时配置应用程序或在关机时进行清理,或者用于保存应用程序范围的对象。这是正确的吗?


答案 1

我会选择无状态 - 服务器可以生成许多bean的实例并并行处理传入的请求。

单例听起来像是一个潜在的瓶颈 - 默认@Lock值为@Lock(WRITE),但对于bean或单个方法,可以更改为@Lock(READ)。


答案 2

根据 ejb 3.1 规范,第 110 页,第 4.8.5 章“单例并发”:

将不支持并发访问的 Java EE 对象(例如实体管理器、有状态会话 Bean 引用)存储在单例 Bean 实例状态中是合法的。但是,Bean 开发人员有责任确保此类对象不会同时被多个线程访问。

此外,根据休眠实体管理器文档

EntityManager 是一个廉价的非线程安全对象,应该只对单个业务流程、单个工作单元使用一次,然后被丢弃。

对我来说,这意味着,你永远不应该将EntityManager注入到单例EJB中。只有当我需要在此类中实现的所有内容都支持并发时,我才会使用单例EJB作为无状态EJB的替代品,而无需执行额外的锁定/同步。由于您或其他程序员迟早可能会从您的关注点中丢失此问题,因此我个人更愿意不使用单例EJB,除非与启动相关的问题或功能可以作为独立单元实现 - 独立于其他bean。从这个意义上说,例如将无状态EJB注入单例似乎并不可取。这样做会引发一个问题,即容器何时实际执行将 SLSB 注入单例的时间点?根据 EJB 3.1 规范第 4.8 章,依赖关系注入在客户端可以访问单例 Bean 实例之前完成。因此,单例显然会坚持SLSB的同一实例,它似乎隐含地成为单例,但似乎没有任何保证。至少我在规范中找不到任何内容,因此行为可能是不可预测的,或者在最好的情况下是特定于容器的,这不是大多数人想要的。

因此,我只会将单例注入单例或单例注入 SLSB,反之亦然。对于将单例注入单例的情况,规范为您提供了定义单例之间的依赖关系的机会,以便容器可以按正确的顺序初始化它们(请参阅 ejb 3.1 规范,第 4.8.1 章,关于@DependsOn注释)。


推荐