JavaEE& JAX-RS:资源类应该声明为单例还是无状态?

时间:2014-11-20 22:21:05

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

查看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资源类中支持其中一个?
  3. 谢谢。

3 个答案:

答案 0 :(得分:3)

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

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

from java-ee-6 tutorial:

  

使用@Lock注释单例类指定所有   单例的业务方法和任何超时方法都将使用   指定锁类型,除非他们用a显式设置锁类型   方法级@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实例是线程安全的。

答案 1 :(得分:1)

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

  

5.5.2原型范围

     

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

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

答案 2 :(得分:1)

首先关闭:在您的情况下,我认为答案应该是&#34;不是&#34;。您的JAX-RS类也没有理由也是EJB。您仍然可以将EJB注入其中(JAX-RS支持此功能),因此在注入的EJB(例如您的VisitDao)中执行所有EJB-ey内容(如数据库事务)并使REST类保持简洁 - 记住:single responsibility principle

@Stateless@Singleton

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

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

  1. 只会创建一个bean实例(所以你不要在多个实例上浪费内存)
  2. 它将在您的应用程序启动后立即创建(因此,一旦您的应用程序正在运行并且请求开始进入,则不需要更昂贵且耗时的资源分配)
  3. 你的bean负责管理并发,即为了线程安全(只是确保你在构造和初始化之后不改变任何bean的字段,你应该没问题 - 这是无论如何都是好的做法)
  4. 顺便说一下,这是Spring中bean的默认设置,我觉得它很好(我有一个Spring背景,所以我可能不会完全不偏不倚)。