在Java Jersey 2 JAX-RS中初始化单例模式

3
我刚接触Jersey(2.22.2),请多关照。我正在创建一个REST服务,与LDAP服务器进行交互,用于存储、删除和检索用户数据。该服务充当安全中介,执行加密/解密操作。
在可以使用REST服务之前,必须进行相当多的初始化工作,我希望只执行一次此初始化(在应用程序部署到服务器时)。因此,此服务将作为单例运行。
希望有人能给我一些指导,告诉我如何最好地实现这个?谢谢!
3个回答

7

Jersey 2.22.2内置了修改资源生命周期的支持。您可以使用@Singleton注释。在官方文档中阅读有关此注释的内容,请访问JAX-RS应用程序、资源和子资源:根资源类的生命周期。简单地将初始化代码放入资源的默认构造函数中。

  • 作用域:单例
  • 注释: @Singleton
  • 完整注释类名: javax.inject.Singleton

在此作用域中,jax-rs应用程序仅有一个实例。可以使用@Singleton注释标注单例资源,其类可以使用Application实例进行注册。您也可以通过将单例实例注册到Application来创建单例。

示例:

package com.airhacks;
import java.util.Date;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("/hello")
@Singleton
public class HelloWorldService {

    public HelloWorldService() throws InterruptedException {
        // Some expensive initialization goes here.
        Thread.sleep(5000);
        System.out.println("Initialized at " + new Date());
    }

    @GET
    public Response getMsg() {
        String output = "Hello world at " + new Date();
        return Response.status(200).entity(output).build();
    }

}

在上面的例子中,由于Glassfish 3上的惰性初始化,第一个请求花费了五秒钟,然后所有后续请求都立即得到服务。

1
您可以使用@Immediate而不是@Singleton,以确保您的服务尽快启动。 曾经有一些问题,Jersey默认情况下未启动Immediate上下文,但我认为现在已经解决了。

你确认这个工作了吗?我谷歌搜索并找到了这个开放的票 - DavidS
看起来它仍然是打开的,但 hk2 无法修复它,必须在 Jersey 层面上进行修复。我会与 Jersey 的人谈论这个问题。 - jwells131313

1
用户使用了Spring框架。

https://projects.spring.io/spring-framework/

https://jersey.java.net/documentation/latest/spring.html

这里有一个完整的工作示例:

https://github.com/jersey/jersey/tree/2.22.2/examples/helloworld-spring-webapp

你只需要在Jersey项目中添加这个依赖,它会自动包含Spring:
        <dependency>
          <groupId>org.glassfish.jersey.ext</groupId>
          <artifactId>jersey-spring3</artifactId>
          <version>${project.version}</version>
        </dependency>

然后您需要在名为applicationContext.xml和src/main/resources的文件中定义Spring Beans:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="mySingletonService" class="com.test.MyService"/>
<beans/>

最后但并非最不重要的,在您的实际资源中,您可以使用@Autowire注释来注入此单例服务:
    @Path("/resource")
@Component
public class MyResource {

    @Autowired
    private MyService myService;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getHello() {
        return myService.sayHello();
    }
}

使用@SingletonResource并在资源内初始化状态是个很糟糕的想法,我认为分离关注点很重要,将状态保留在REST资源中就显得非常糟糕。通过创建LDAPResource和LDAPService来分离处理接口(REST)和业务逻辑的代码。Spring在此处所扮演的角色只是你自己需要实例化的连线工作。

这不是一个好的答案。它只是说“使用Spring框架”,并链接到一些文档,而没有解释它如何回答用户的问题。 - DavidS
@DavidS Spring框架是否更适合这个项目?如果最适合的是放弃Jersey,我也愿意接受。 - Wes
@Wes,Spring是一个支持许多用例的框架。Jersey是一个用于创建REST服务的小型库。我无法预测你的未来,但我建议你先从Jersey开始。我的回答表明,在这一点上,Spring将是一个不必要的依赖项:Jersey已经支持你想做的事情。 - DavidS
这个编辑过的答案仍然没有解释如何创建一个_singleton_ REST资源,或者以其他方式只执行一次昂贵的初始化。 - DavidS
好的,请将Ulises的答案标记为正确的,@Wes。 - DavidS
显示剩余3条评论

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