30

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 用于在启动时配置应用程序或在关闭时清理或保存应用程序范围的对象。这个对吗?

4

8 回答 8

13

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

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

于 2010-01-06T16:05:17.550 回答
4

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

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

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

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

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

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

于 2014-07-31T13:13:56.630 回答
2

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

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

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

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

于 2015-07-27T20:51:51.963 回答
1

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

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

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

于 2012-01-03T15:26:55.880 回答
0

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

于 2013-09-27T13:40:52.827 回答
0

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

于 2013-07-05T06:24:44.173 回答
0

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

于 2014-06-23T18:28:14.210 回答
0

恕我直言,我会这样回答:

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

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

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

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

于 2017-06-16T08:40:23.070 回答