Getting Started with JEE 6 – CDI Qualifiers

In the last article we developed an application that had two alternative implementations of an EJB based QuoteService which unfortunately ran aground because CDI couldn’t choose which of our implementation to use. In this article I’ll discuss how to let the system know which implementation to use by using CDI qualifiers.

A CDI qualifier is nothing more than an annotation that is added to a concrete implementation of an interface that allows the CDI system to pick which one should be used when performing injection. Without making a fuss the system has actually been using one qualifier already, the @Any qualifier (if you look at the error message in the previous article you’ll see it mentioned).

To get started create a new qualifier called Substitute like this:

package example.simpleproject;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

@Retention( RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Qualifier
public @interface Substitute {
	
}

Note the unusual @interface – this isn’t a regular interface this is an annotation. The first two annotations tell the system that this annotation should survive to be read at runtime and that it can be used on parameters, fields, types and methods. In the example here we only inject into fields but injection can be carried elsewhere so this covers all the options. The third annotation, @Qualifier, is the one that CDI is interested in as it tells the system that this is a CDI qualifier annotation. Annotate the AlternativeQuoteService with the @Substitute annotation and try the test page again (you may need a restart I did).

package example.simpleproject;

import javax.ejb.Stateless;

@Stateless
@Substitute
public class AlternativeQuoteService implements QuoteService {

	@Override
	public String getQuote() {
		return "A nickel ain't worth a dime anymore - Yogi Berra";
	}
	
}

Notice that now, although we have two implementations of QuoteService, we no longer get an exception and CDI has selected the SimpleQuoteService for injection. This works because the in the absence of any qualifier there is an implicit @Any qualifier which indicates that the class should be the default for injection. The exception was being caused because we had two classes that had implicit @Any annotations. Once we had annotated the AlternativeQuoteService with @Substitute CDI was able to decide which one to inject for us – it wasn’t explicitly told to use @Substitute so it chose @Any.

Picking an Alternative

That’s all well and good but what if we actually want to use our alternative implementation? That’s as simple as modifying the @Inject annotation in the QuoteBean class like this:

package example.simpleproject;

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class QuoteBean {
	
	@Inject
	@Substitute
	private QuoteService quoteService;
	
	public String getQuote() {
		return quoteService.getQuote();
	}
	
}

By placing our @Substitute annotation on the injected member we are telling CDI to select an instance that matched both the class type and the qualifier if possible. If you refresh the test page you should now see the quote from the AlternativeQuoteService.

In the net article I’ll look a how we can select which implementation to use at runtime rather than build time.