Jersey 2 singleton dependency injection creates multiple instances

落爺英雄遲暮 提交于 2020-01-13 19:07:32

问题


Here I have a singleton, that I whant to inject to my application

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

}

Here's how I register it:

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

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

    //.....

Finally, I print the nuber of singletons in request:

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

It returns two instances. I understand that Jersey uses reflections to access private constructor and create another instance. Why is this happening and how do I prevent this?


回答1:


The getSingletons has nothing to do with injections. It is meant to register singleton JAX-RS components (i.e. resources classes and providers), which don't actually need to be "classic" singletons. They can just be an instance of a regular class.

To handle injection of arbitrary components/services with Jersey 2.x, see Custom Injection and Lifecycle Management.

The general pattern is to create a Factory<T> implementation, with T being the injectable type. Then the factory needs to be registered with the Jersey runtime. One way to do that is through an AbstractBinder. For example

public class MyContextProvider implements Factory<MyContext> {

    @Override
    public MyContext provide() {
        return new MyContext();
    }

    @Override
    public void dispose(Bar bar) {}
}

Then bind it in your subclass of ResourceConfig (which is a subclass of 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);
            }
        });
    }
}

The packages method allows for scanning of the package and sub-packages for resource classes (classes annotated with @Path) and providers (classes annotated with @Provider), so you don't need to explicitly register them.

You will also need to make sure you have all the needed compile-time dependencies. If you are using Maven, just use

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

The provided scope is for if you are using Glassfish, as Glassfish alredy has the jars. You don't want to duplicate the jars with different version. If you are just in a servlet container like Tomcat, you can remove the <scope>. If you are not using Maven, then you need to add the jars manually from the Jersey JAX-RS 2.0 RI bundle. And likewise, if you are in Glassfish, you need to make the jars only compile-time jars. You do not want to include them into the build of the war.




回答2:


You can add this in your constructor :

if (instance == null)
   instance = this;


来源:https://stackoverflow.com/questions/32192309/jersey-2-singleton-dependency-injection-creates-multiple-instances

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!