CDI – Selecting an implementation by using AnnotationLiteral

CDI – Selecting an implementation by using AnnotationLiteral

Let’s assume, that we have two or more service instances of a service interface and we need to select a appropriate instance by a dynamic configuration value at runtime. All service instances are annotated with a unique Qualifier. To select the specific instance an AnnotationLiteral implementation can be used. AnnotationLiteral supports the instantiation of annotation type instances.

The example has a simple Service Interface:


public interface ColorService {

    String getColor();

}

And two implementations:

import javax.inject.Named;

@Named("red")
public class RedColorService implements ColorService {

    @Override
    public String getColor() {
        return "red";
    }
}

and

import javax.inject.Named;

@Named("blue")
public class BlueColorService implements ColorService {

    @Override
    public String getColor() {
        return "blue";
    }
}

Both services are annotated with @Named  so the AnnotationLiteral  implements the Named annotation interface:

import javax.enterprise.inject.Instance;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.inject.Named;


public class ColorSelector {

    private static final String DEFAULT_COLOR = "red";

    @Inject
    private Instance<ColorService> colorServices;

    public ColorService selectColorService(String colorName) {
        Instance<ColorService> colorServiceInstance = colorServices.select(new NamedLiteral(colorName));
        if (!colorServiceInstance.isUnsatisfied()) {
            return colorServiceInstance.get();
        } else {
            return colorServices.select(new NamedLiteral(DEFAULT_COLOR)).get();
        }
    }

    private class NamedLiteral extends AnnotationLiteral<Named> implements Named {

        private String value;

        NamedLiteral(String value) {
            this.value = value;
        }

        @Override
        public String value() {
            return value;
        }
    }
}

Now the specific Service implementation is selected by it’s name:

@Inject
private ColorSelector colorSelector;

...
ColorService colorService = colorSelector.selectColorService("blue");
colorService.getColor();
...

The key benefit of this method is that the caller only needs a string to select the specific service. This is useful in situations where service implementations are added or removed by dependent jar libraries of the whole application.