【问题标题】:JavaEE & JAX-RS: Resource Class Should Be Declared as Singleton or Stateless?JavaEE 和 JAX-RS:资源类应该声明为单例还是无状态?
【发布时间】:2014-11-20 22:21:05
【问题描述】:

查看following tutorial 的列表 #1,

JAX-RS 资源类可以定义为@Stateless 或@Singleton。

我的应用程序中有以下代码:

@Stateless
public class VisitDaoImpl implements VisitDao {

    @PersistenceContext(name = "MysqlPU")
    private EntityManager em;

    @Override
    public void persist(Visit vist) {
        em.persist(vist);
    }
}

@ApplicationPath("rest")
public class ApplicationConfig extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new java.util.HashSet<>();
        resources.add(WelcomeResource.class);
        return resources;
    }
}

//@Singleton
@Stateless
@Path("/Welcome")
public class WelcomeResource {

    @EJB
    private VisitDao visitDao;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String wellcomeMessage() {
        visitDao.persist(new Visit())
        return "Welcome";
    }
}

如您所见,我的资源类中除了 Dao bean 之外没有状态。

我的问题是:

  1. 我应该在这里使用@Stateless 还是@Singleton bean?
  2. 在 JAX-RS 资源类中何时优先考虑其中一个?

谢谢。

【问题讨论】:

    标签: jakarta-ee ejb jax-rs java-ee-6 java-ee-7


    【解决方案1】:

    如果你想对数据库执行写操作或其他影响状态的方法,我认为@Stateless。

    多个线程(客户端)必须等待单例可用,因为默认锁是写入单例-ejbs。

    from java-ee-6 tutorial:

    使用@Lock 注释单例类指定所有 单例的业务方法和任何超时方法都将使用 指定的锁类型,除非他们明确设置锁类型 方法级别的@Lock 注释。如果不存在 @Lock 注释 应用了单例类,默认锁类型@Lock(WRITE) 到所有业务和超时方法。

    在示例中,@Path-ejb 和数据库之间有一个 ejb-dao。这表明您可以更改为 @Singleton 并在您的业务方法中使用 @Lock(READ) (不知道为什么有人想尝试)。

    但我认为这并不安全,因为相同的 ejb-dao-instance 将用于客户端完成的所有并发调用,并且 DAO 持有的 EntityManager 不是线程安全的:

    from java-ee-6 tutorial:

    当应用程序需要时使用应用程序管理的实体管理器 访问不使用 JTA 传播的持久性上下文 在特定持久性中跨 EntityManager 实例的事务 单元。在这种情况下,每个 EntityManager 都会创建一个新的、隔离的 持久性上下文。 EntityManager 及其相关的持久性 上下文由应用程序显式创建和销毁。 他们 直接注入EntityManager实例的时候也不能用 因为 EntityManager 实例不是线程安全的。 EntityManagerFactory 实例是线程安全的。

    【讨论】:

      【解决方案2】:

      在 Spring 中将无状态对象推荐为 Singleton 的方法,请查看 questionSpring current guide。 因此,根据 spring,您应该使用 Singleton,因为您的 Rest Service 是无状态的。

      5.5.2 原型范围

      bean 部署的非单例原型范围导致 每次请求特定的 bean 时创建一个新的 bean 实例 豆子做好了。即 bean 被注入另一个 bean 或者你 通过容器上的 getBean() 方法调用来请求它。作为一个 规则,对所有有状态的 bean 和单例使用原型范围 无状态 bean 的范围。

      但根据Enterprise Beans Java Tutorial,您可以使用无状态 bean 或单例,因为它们明确声明“bean 实现了 Web 服务”。对于无状态和单身人士。但是由于singleton pitfalls,无状态bean 可能是更好的选择。最后,在 Spring 中使用单例,在 JavaEE 中使用无状态 bean。

      【讨论】:

      • 在 java-ee 中,@Stateless bean 是池化的,因此构建它们的成本并不像 spring 中的原型范围 bean 那样昂贵
      • 没关系,由于垃圾回收,即使被池化,也不能像singleton那么便宜。如果可以的话,你应该选择单例。
      • 不,但在 java-ee 中,@Stateless beans 的作用涵盖了 spring-prototype-scoped beans 提供的许多用例。 java-ee Singleton-ejb 适用于您实际上想要在 bean 中存储状态的情况
      • 你能给我一个参考吗?我会在阅读后更改我的答案。
      • 这并不完美,但稍微描述了角色:docs.oracle.com/javaee/6/tutorial/doc/gipjg.html
      【解决方案3】:

      首先:在你的情况下,我认为答案应该是“都不是”。您的 JAX-RS 类没有理由也是 EJB。您仍然可以将 EJB 注入其中(JAX-RS 支持这一点),所以在注入的 EJB(例如您的VisitDao)中执行所有 EJB 结构(例如数据库事务)并保持您的 REST 类小而简单 - 请记住:@ 987654321@.

      @Stateless@Singleton

      我发现人们倾向于使用@Stateless。就像在其他答案中解释的那样,这会导致 EJB 池,因为默认情况下 EJB 假定您的类不是线程安全的,因此它永远不会允许多个线程同时进入单个实例。池的大小由您的应用服务器配置控制。如果要访问会话 bean 的线程多于池中的可用实例(并且池的最大大小不允许创建更多实例),则线程需要等待。

      由于在高流量情况下运行时存在线程争用的风险,再加上如果您正确地对会话 bean 进行编程,它们实际上是线程安全的,我已经开始转到我将 EJB 注释为 @Startup @Singleton ConcurrencyManagement(ConcurrencyManagementType.BEAN) 的模式。这意味着:

      1. 只会创建 bean 的一个实例(因此您不会在多个实例上浪费内存)
      2. 它将在您的应用程序启动后立即创建(因此,一旦您的应用程序正在运行并开始收到请求,就不会再分配昂贵且耗时的资源)
      3. 您的 bean 负责管理并发性,即线程安全(只要确保在构造和初始化后不更改 bean 的任何字段,您应该没问题 - 无论如何这是一个好习惯)李>

      顺便说一句,这是 Spring 中 bean 的默认设置,我认为这是一个不错的选择(我有 Spring 背景,所以我可能对此并不完全不偏不倚)。

      【讨论】:

        猜你喜欢
        • 2019-06-29
        • 1970-01-01
        • 2017-12-09
        • 2019-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多