I am using HK2 to resolve dependencies of services in my Jersey / Jetty web service. I have a situation where for one particular interface, I want to use a specific implementation as a "default" implementation. By "default" I mean no names or qualifiers - it is what you get if you do not specify any annotations on top of the field or argument. However, in a few very specific situations, I want to provide an alternative implementation that will be qualified with an annotation.
As a result of my experimentation I have actually got this to work reliably by using the ranked()
qualifier in my bindings. It appears the highest rank becomes the default. However, I do not understand why it works, and I am concerned I am writing code that depends on an undocumented implementation detail of HK2 that could change when we update versions.
Here's a contrived example of the interesting parts of what I am doing. Is ranked()
what I should be using to specify "default" and annotated variants of a service? Should I be using another technique?
public interface IFoo {
public String getString();
}
public class DefaultImpl implements IFoo {
public String getString() {
return "Default Implementation";
}
}
public class AnnotatedImpl implements IFoo {
public String getString() {
return "Annotated Implementation";
}
}
public class Bindings extends AbstractBinder {
@Override
public void configure() {
ServiceBindingBuilder<DefaultImpl> defaultImpl =
bind(DefaultImpl.class)
.to(IFoo.class);
defaultImpl.ranked(9);
ServiceBindingBuilder<AnnotatedImpl> annotatedImpl =
bind(AnnotatedImpl.class)
.qualifiedBy(new MyAnnotationQualifier())
.to(IFoo.class);
annotatedImpl.ranked(1);
}
}
public class MyService {
@Inject
public MyService(
IFoo defaultImplementation,
@MyAnnotation
IFoo annotatedImplementation) {
// ... my code here ...
}
}
I stumbled across some documentation on HK2's website that is consistent with the behavior that I am seeing.
If there are more than one Widget (e.g. Widget is an interface that can have many implementations) then the best Widget will be returned from the
getService
method.Services are sorted by (in order) the service ranking, the largest locator id (so that services in children are picked before services in parents) and smallest service id (so that older services are picked prior to newer services). Therefore the best instance of a service is a service with the highest ranking or largest service locator id or the lowest service id. The ranking of a service is found in its
Descriptor
and can be changed at any time at run time. The locator id of a service is a system assigned value for theDescriptor
when it is bound into theServiceLocator
and is the id of thatServiceLocator
. The service id of a service is a system assigned value for theDescriptor
when it is bound into theServiceLocator
. The system assigned value is a monotonically increasing value. Thus if two services have the same ranking the best service will be associated with the oldestDescriptor
bound into the system.
Therefore, I am using ranked()
on my bindings correctly. It is one of the two ways to control what HK2 defines as the "default" (or "best") service to inject into my dependent services.