无状态会话 Bean 与单例会话 Bean

Posted

技术标签:

【中文标题】无状态会话 Bean 与单例会话 Bean【英文标题】:Stateless Session Beans vs. Singleton Session Beans 【发布时间】:2011-01-02 02:34:56 【问题描述】:

Java EE 6 Tutorial 说:

为了提高性能,如果无状态会话 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 实例并并行处理传入请求。

Singleton 听起来像是一个潜在的瓶颈——默认的@Lock 值是@Lock(WRITE),但对于 bean 或个别方法可能会更改为 @Lock(READ)。

【讨论】:

你能解释一下,如果我们可以对所有类使用@Lock(LockType.READ),为什么你认为单例是一个瓶颈?例如@Lock(LockType.READ)public class MySingleton ... 我想知道这是否真的是无状态的缺点,mjn。使用无状态,我们是否需要一个大小为 N 的池来服务 N 个并发请求?大概一个带有@Lock(READ) 的 Singleton 可以在不调整池大小的情况下处理 N 个并发请求。【参考方案2】:

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

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

此外,根据hibernate entitymanager documentation

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

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

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

【讨论】:

【参考方案3】:

@Stateless 将允许您在 JVM 中准备好多个副本(在内存和池大小允许的情况下),其中 @Singleton 在 JVM 中只有一个副本,即使单个副本可以支持多个针对它运行的并发线程。

在性能方面@Singleton 会更好,前提是它使用的资源允许长时间运行访问。但是,在分布式环境中,有时会发生不好的事情,例如数据库或网络链接可能会失败。

使用@Stateless bean,访问时间更短。此外,如果出现故障,它将重新生成并尝试建立与资源的新连接。如果在单例上发生了这样的事情,那么由单例来处理它而无需重新启动应用程序,因为每个 JVM 只调用一次 @PostConstruct。

在大多数情况下,尤其是在我无法控制的系统上,我更喜欢容错性而不是性能。

【讨论】:

【参考方案4】:

我认为 Singleton 在并发使用上的表现不会比 SLSB Pool 差,它可能会更好。唯一的问题是如果你想在线程之间共享一些东西,你需要锁定它,这可能是一个很大的性能问题。因此,在这种情况下,SLSB 池的性能要好得多,因为它不是 100% 单例,有更多实例,一个被锁定,另一个出现。无论如何,如果锁定是所有 SLSB 共享的某些资源,那么池也无济于事。

简而言之,我认为单例比 SLSB Pool 更好,如果可以的话,你应该使用它。它也是 Spring Beans 的默认作用域。

我不是JavaEE专家,这只是我的感觉,如果我错了,请纠正我。

【讨论】:

【参考方案5】:

我认为你应该使用单例会话 bean。因为登录服务应该是一个全局服务,它不需要为具体的用户或调用存储任何状态。

【讨论】:

【参考方案6】:

如果您确定没有在线程之间共享状态,那么单例就可以了,在这种情况下,您还应该使用 @ConcurrencyManagement( ConcurrencyManagementType.BEAN ) 注释类,这将允许多个线程同时运行。

【讨论】:

【参考方案7】:

如果您有任何资源将在整个应用程序中保持不变,您应该选择 Singleton。就像从某个文件或引用数据中加载一些数据,这些数据在应用程序生命周期中不会改变。否则,请选择 SLSB。 SLSB 的缺点是会创建多个对象,因此会占用更多内存。

【讨论】:

【参考方案8】:

恕我直言:

“没有状态必须在应用程序之间共享”导致我使用无状态 bean,因为这句话“为了提高性能,您可能会选择无状态会话 bean...”。

考虑到“单个企业 bean 可以被多个线程同时访问”,您将不得不使用单例。如果我做对了,在正确使用时甚至不可能同时访问无状态 bean。

“不需要在启动或击落时执行任何任务”对我来说并不重要。如果必须完成任务才能正确设置 bean,则必须通过 @PostActivate 方法调用它们。

既然您要求并发访问,我会合乎逻辑地总结您的问题,@Singleton 应该使用什么。当然,您必须手动控制对任何其他资源(不是 EJB)的访问同步。

【讨论】:

以上是关于无状态会话 Bean 与单例会话 Bean的主要内容,如果未能解决你的问题,请参考以下文章

一篇博客带你拿下吉林大学JAVAEE期末(七:会话Bean)

一篇博客带你拿下吉林大学JAVAEE期末(七:会话Bean)

一篇博客带你拿下吉林大学JAVAEE期末(七:会话Bean)

为啥要池化无状态会话 bean?

有状态bean和无状态的bean

如何在无状态会话 bean 中创建多线程?