问题
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