【问题标题】:What is the best way to inject a singleton service into a JAX-RS/Jersey resource?将单例服务注入 JAX-RS/Jersey 资源的最佳方法是什么?
【发布时间】:2020-07-31 14:39:37
【问题描述】:

例如,如果多个资源端点需要访问某些消息总线来处理请求怎么办?当服务类本身不是资源而是由资源使用时,当然有一些方法可以注册单例服务类并将其注入资源中。
我在提供程序或自定义 HK2 绑定中看到的所有示例都涉及资源。

我找到的最接近我正在寻找的东西是这个问题:
Trouble creating a simple singleton class in Jersey 2 using built-in Jersey dependency injection

执行此操作的最佳 JAX-RS/Jersey 方式是什么?

请注意,编程方式最有用,我没有使用 xml 文件来配置服务器。

【问题讨论】:

  • 如果您的 JAX-RS 资源也是单例,那么这个答案很好。否则,除了这个答案之外,您还必须处理多线程
  • 谢谢@Paul。你能详细说明线程问题吗?你的意思是多个请求将同时由同一个资源对象提供服务吗?
  • 是(15 个字符)。
  • 如果资源不是单例,则为每个请求创建一个新的,但您注入的服务只有一个实例。当心任何并发陷阱。如果所有方法都相互独立,那么你应该没问题。这就是我要说的

标签: jersey jax-rs jersey-2.0 hk2


【解决方案1】:

答案归于@areus the answer provided here
但是,我提供了自己的答案,以便我可以共享代码。

服务 Bean

@Singleton
public final class MyServiceBean
{
  private static final AtomicInteger INSTANCES = new AtomicInteger();
  private final AtomicInteger calls = new AtomicInteger();

  public MyServiceBean()
  {
    INSTANCES.incrementAndGet();
  }

  public String getMessage()
  {
    return String.format("MyServiceBean{INSTANCES=%d, CALLED=%d}", INSTANCES.get(), calls.incrementAndGet());
  }
}

资源类

@Path("/messages")
public final class MyResource
{
  @Inject
  private MyServiceBean bean;

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public Response handle()
  {
    return Response.ok(this.bean.getMessage())
      .type(MediaType.TEXT_PLAIN_TYPE)
      .build();
  }
}

HK2 活页夹

public final class MyServiceBeanBinder extends AbstractBinder
{
  @Override
  protected void configure()
  {
    bind(MyServiceBean.class).to(MyServiceBean.class).in(Singleton.class);
  }
}

然后像这样注册活页夹和资源:

final ResourceConfig config = new ResourceConfig();
config.register(MyResource.class);
config.register(new MyServiceBeanBinder());

启动服务器并多次访问资源产生:

MyServiceBean{INSTANCES=1, CALLED=1}   
MyServiceBean{INSTANCES=1, CALLED=2}   
MyServiceBean{INSTANCES=1, CALLED=3}   
MyServiceBean{INSTANCES=1, CALLED=4}   
MyServiceBean{INSTANCES=1, CALLED=5}

【讨论】:

  • 如果我从 MyserviceBean 中删除单例注释,这仍然有效吗?因为据我所知,Binder 中链式 '.in()' 方法中明确提到的单例实际上跨请求创建和使用单个对象?
  • 是的,它在技术上是多余的(尽管我有时喜欢它记录课程的意图)。 HK2 binder 对“in(Singleton.class)”的调用优先于注解。
【解决方案2】:

如果您的平台支持 EJB,您可以使用 @Singleton EJB(javax.ejb 包,而不是 javax.inject),并使用 @EJB 注释将其注入到您的资源中。单例 EJB 还具有开箱即用的并发访问控制。

在普通的 Jersey 上,您可以使用 CDI 应用程序上下文。使用@ApplicationScoped 注释声明服务类,并使用@Inject 将其注入您的资源。 CDI 只会实例化一个 bean。

如果您无法对服务类进行注释,您可以创建一个方法,为您的服务实现提供注释,使用@Produces@ApplicationScoped

@Produces
@ApplicationScoped
public MyService produceService() {
    // instantiate your service client
}

然后在你的资源上使用它:

@Inject
private MyService

【讨论】:

  • 请注意,这需要一个 EJB 容器,原因很明显。由于他专门询问 JAX-RS/Jersey,我假设他没有使用成熟的 JavaEE 堆栈。
  • 感谢您的建议。不过,我们确实没有使用 EJB,只是使用带有嵌入式 Jetty 的 Jersey 库存。
  • 提供了使用 CDI 的替代方案
  • 这个想法很有效,而且比添加功能更简单。
猜你喜欢
  • 2017-12-09
  • 1970-01-01
  • 1970-01-01
  • 2015-02-07
  • 1970-01-01
  • 2015-05-05
  • 1970-01-01
  • 1970-01-01
  • 2013-04-24
相关资源
最近更新 更多