【问题标题】:Jersey 2 singleton dependency injection creates multiple instancesJersey 2 单例依赖注入创建多个实例
【发布时间】:2015-08-24 21:49:10
【问题描述】:

这里我有一个单例,我想将它注入到我的应用程序中

@Singleton
@Path("singleton-bean")
public class MyContext {

    private MyContext() {
        instances++;
    }

    private static MyContext instance;

    public static MyContext getInstance(){
        if (instance == null)
            instance = new MyContext();
        return instance;
    }

    public static int instances = 0;

}

我是这样注册的:

@ApplicationPath("webresources")
public class ApplicationConfig extends Application {

    @Override
    public Set<Object> getSingletons() {
        final Set<Object> singletons = new HashSet<>();
        singletons.add(MyContext.getInstance());
        return singletons;
    }

    //.....

最后,我打印请求中的单例数:

@Path("foo")
public class Foo {

    @Inject
    public MyContext message;

    @GET
    public String index() throws UnknownHostException {
        return String.format("%s number of instances: %s", message, MyContext.instances);
    }

它返回两个实例。我了解 Jersey 使用反射来访问私有构造函数并创建另一个实例。为什么会发生这种情况以及如何防止这种情况发生?

【问题讨论】:

    标签: java dependency-injection jersey singleton jersey-2.0


    【解决方案1】:

    getSingletons 与注射无关。它旨在注册单例 JAX-RS 组件(即资源类和提供者),实际上不需要是“经典”单例。它们可以只是普通类的一个实例。

    要使用 Jersey 2.x 处理任意组件/服务的注入,请参阅 Custom Injection and Lifecycle Management

    一般模式是创建一个Factory&lt;T&gt; 实现,T 是可注入类型。然后工厂需要注册到 Jersey 运行时。一种方法是通过AbstractBinder。例如

    public class MyContextProvider implements Factory<MyContext> {
    
        @Override
        public MyContext provide() {
            return new MyContext();
        }
    
        @Override
        public void dispose(Bar bar) {}
    }
    

    然后将其绑定到ResourceConfig 的子类(Application 的子类)中。

    @ApplicationPath("/webresources")
    public class AppConfig extends ResourceConfig {
    
        public AppConfig() {
    
            packages("com.stackoverflow.jersey");
    
            register(new AbstractBinder(){
                @Override
                protected void configure() {
                    bindFactory(MyContextProvider.class)
                            .to(MyContext.class)
                            .in(Singleton.class);
                }
            });
        }
    }
    

    packages 方法允许扫描包和子包中的资源类(带有 @Path 注释的类)和提供者(带有 @Provider 注释的类),因此您无需显式注册它们.

    您还需要确保拥有所有需要的编译时依赖项。如果您使用的是 Maven,请使用

    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>2.19</version>
        <scope>provided</scope>
    </dependency> 
    

    provided 范围适用于您使用 Glassfish 的情况,因为 Glassfish 已经有罐子了。您不想复制具有不同版本的罐子。如果您只是在 Tomcat 之类的 servlet 容器中,则可以删除 &lt;scope&gt;。如果您不使用 Maven,则需要从 Jersey JAX-RS 2.0 RI bundle 手动添加 jar。同样,如果您在 Glassfish 中,则需要使 jars 仅是编译时 jars。您不想将它们包含在战争的构建中。

    【讨论】:

      【解决方案2】:

      你可以在你的构造函数中添加这个:

      if (instance == null)
         instance = this;
      

      【讨论】:

      • 当 jersey 将使用构造函数实例化您的类时,它将存储在实例中。然后,当您获取实例时,它将返回由球衣创建的对象,而不是创建一个新对象。
      • 你只是将一个静态指针移动到一个新对象上,这并不会改变正在创建多个实例的事实。
      • 如果你不调用 getInstance 方法,你会有 2 个实例吗?
      • 好的,我以为一个是由球衣(反射)创建的,另一个是通过调用 getInstance 方法创建的。
      • 没错,但您的修复不会改变正在创建多个实例的事实。
      猜你喜欢
      • 1970-01-01
      • 2016-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多