javadependency-injectionjerseydependency-managementhk2

How do I define a "default" implementation in HK2?


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 ... 
    }
}

Solution

  • 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 the Descriptor when it is bound into the ServiceLocator and is the id of that ServiceLocator. The service id of a service is a system assigned value for the Descriptor when it is bound into the ServiceLocator. 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 oldest Descriptor bound into the system.

    Source

    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.