将单例服务注入 JAX-RS/Jersey 资源的最佳方法是什么?

What is the best way to inject a singleton service into a JAX-RS/Jersey resource?

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

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

最好的 JAX-RS/Jersey 方法是什么?

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

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

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

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

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

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

@Inject
private MyService

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

服务 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());
  }
}

资源Class

@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}