JAX-RS:无状态、单例、请求范围混淆

5

我曾多次看到 JAXRS 资源的配置方式有多种。

有时我看到它们被注释为 @Singleton@Stateless@ApplicationScoped@RequestScoped,甚至没有任何注释或同时使用两者。

  • javax.enterprise.context.RequestScoped
  • javax.enterprise.context.ApplicationScoped
  • javax.ejb.Stateless
  • javax.ejb.Singleton
  • javax.inject.Singleton

我应该使用哪个注释?

javax.ejb 和 JAXRS 资源有什么关系?

另一方面,我还想了解如何准确使用 @Context 注释。

我的意思是,我曾经看到这个应用于参数,也应用于类字段

@Path("entity")
public class EntityResource {

    @Context
    private Request request;

    @POST
    public Response create(Entity entity) {
        this.request...
    }

}

或者,
@Path("entity")
public class EntityResource {

    @POST
    public Response create(Entity entity, @Context Request request) {
        request...
    }

}

我该如何继续?

3个回答

7
在JAX-RS资源类中,除非您想在同一类中使用EJB或CDI功能,否则无需使用任何EJB或CDI注释。
如果您想将任何CDI Bean注入到资源类中,则资源类本身必须是CDI Bean,因此应添加作用域注释,最好是@javax.enterprise.context.RequestScoped。
如果您使用像@Stateless这样的EJB注释,注入也将起作用,因为EJB也是CDI Bean(但反过来不是)。但是,stateless bean有不同的生命周期,并且默认情况下是事务性的。
另一方面,如果您需要事务,则还可以将@Transactional与@RequestScoped和@Path结合使用。
背景:
对于大多数目的而言,EJB已经有些过时了。它们在JAX-RS和CDI之前首次出现,但如今,CDI被认为是Java EE / Jakarta EE中统一的依赖注入机制,并正在更新旧规范以更紧密地集成CDI。

1
为什么“最好使用RequestScoped”? - user1075613
4
每个JAX-RS资源方法调用都对应一个HTTP请求。如果您使用更长寿命的范围,您的类必须是线程安全的。 - Harald Wellmann
关于这个问题:“如果你想将任何CDI bean注入到资源类中,那么资源类本身必须是一个CDI bean,因此你应该添加一个作用域注释”:这并不完全正确,JAX-RS资源和提供者不是CDI bean,因为它们不是由CDI实例化的,而是由JAX-RS实例化的。出于同样的原因,它们不需要作用域注释:JAX-RS以硬编码的方式决定它们的作用域(资源的请求范围,提供者的应用程序范围)。JAX-RS与CDI集成在一起,意味着它允许这些对象具有CDI管理的依赖项。 - jsiwrk
请参考此答案:https://dev59.com/eXTYa4cB1Zd3GeqP0_hE#17911683 - jsiwrk

2
关于您的第一个问题(托管Bean范围),我认为不应该使用任何范围注释,因为Java-EE容器将根据Jax-rs注释自动管理Bean的正确范围。
然而IBM的人似乎对这个问题有不同的看法,并陈述了以下内容:
最佳实践
在启用JCDI的存档中添加特定的生命周期范围到任何存在于您的应用程序中的JAX-RS根资源和提供程序类中。对于具有@javax.ws.rs.Path注释的JAX-RS资源类,您可以使用@javax.enterprise.context.RequestScoped。对于javax.ws.rs.core.Application子类和@javax.ws.rs.ext.Provider注释类,您必须使用@javax.enterprise.context.ApplicationScoped。
关于@Context注释,它用于注入与当前HTTP请求相关的对象(您可以在此处找到可注入实例的列表),并且可以在实例字段和方法参数上使用它。

JAX-RS资源可以是EJB资源吗? - Jordi
Jax-rs有自己的容器https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/package-summary.html,似乎是在servlet容器之上实现的https://dev59.com/9FgR5IYBdhLWcg3wXcam。老实说,当您在类上放置jax-rs和EJB注释时,我不知道哪个容器管理bean,我怀疑EJB注释被忽略了,但这取决于您的服务器实现。 - Gab
1
“Java-EE容器不会自动根据Jax-rs注释为您的bean管理正确的作用域” - 这是不正确的。 JAX-RS具有自己的范围概念(请参见javax.ws.rs.core.Application.getClasses() vs. getSingletons()),但是默认情况下,JAX-RS资源不是CDI bean,除非您添加bean定义注释或使用bean-discovery-mode =“all”。 - Harald Wellmann
我应该如何设置JAX-RS资源为CDI bean?是否有任何与CDI相关的注释bean?@Stateless在微配置文件中可用吗? - Jordi
没有@Stateless是EJB规范的一部分,它不是微配置文件的一部分。正如Herald所述,您可以使用任何CDI“bean defining注释”,首选作用域注释(@ApplicationScoped@SessionScoped@ConversationScoped@RequestScoped)(否则该Bean将使用 @Dependent伪作用域)。 - Gab

0

在 JAX-RS 资源上使用 @Stateless 可以很好地防止 Web 服务器为每个调用创建新的资源(使用 @RequestScoped 注释时会这样做)。 当使用 @Stateless 注释时,服务器将创建一个可配置的资源池。 您还将获得事务的好处。


那么ApplicationScoped呢?我的意思是,@ApplicationScoped@Stateless之间有什么区别呢? - Jordi
@Jordi:使用池(即使用@Stateless),您可以控制应用程序上的流量(因为单个http调用将附加到单个无状态资源)。但是,对于应用程序范围的bean,您无法控制传入的请求流量。没有一个比另一个更好,这取决于您想要做什么。 - Rouliboy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接