Java EE 6 CDI Qualifiers explained - @Default, @Any, @New, @Named

The CDI specification (JSR-299) defines "Qualifer" as a means to uniquely identify one of the multiple implementations of the bean type to be injected. The spec defines certain built-in qualifiers (@Default, @Any, @Named, @New) and new qualifiers can be easily defined as well. This Tip Of The Day (TOTD) discusses the in-built qualifiers and how they can be used.
The @Named qualifier makes a bean EL-injectable, that's it!
The @Default qualifier, appropriately called as "default qualifier", exists on all beans without an explicit qualifer, except @Named. Consider the following bean type and implementation:
public interface Greeting {
    public String greet(String name);
}


public class SimpleGreeting implements Greeting {
    public String greet(String name) {
        return "Hello " + name;
    }
}

So SimpleGreeting defined above is equivalent to:
@Default
public class SimpleGreeting implements Greeting {
    . . .
}
Similarly
@Named
public class SimpleGreeting implements Greeting {
    . . .
}
is equivalent to:
@Named
@Default
public class SimpleGreeting implements Greeting {
    . . .
}
The default qualifier works for the type injection as well. So
@Inject Greeting greeting;
and
@Inject @Default Greeting greeting;
are equivalent. However it is not recommended to use @Named as injection point qualifier, except in the case of integration with legacy code that uses string-based names to identify beans.
@Any is another in-built qualifier on all beans, including the ones with implicit or explicit @Default qualifier, except @New qualified beans (more on this later). So the SimpleGreeting implementation is equivalent to:
@Default @Any
public class SimpleGreeting implements Greeting {
    . . .
}
And can be injected as:
@Inject @Any Greeting greeting;
or even
@Inject @Any @Default Greeting greeting;
Now lets add a new implementation of the Greeting as:
public class FancyGreeting implements Greeting {
    public String greet(String name) {
        return "Nice to meet you, hello " + name;
    }
}
Now all of the following injections fail:
@Inject Greeting greeting;
@Inject @Default Greeting greeting;
@Inject @Any Greeting greeting;
@Inject @Default @Any Greeting greeting;
with the "ambiguous dependencies" error because both the implementations now have @Default and @Any qualifiers.
This can be resolved by adding a new qualifier called @Fancy to the FancyGreeting implementation as:
@Fancy
public class FancyGreeting implements Greeting {
    . . .
}
which is also equivalent to:
@Fancy @Any
public class FancyGreeting implements Greeting {
    . . .
}
Now all the following inject statements:
@Inject Greeting greeting;
@Inject @Default Greeting greeting;
@Inject @Any @Default Greeting greeting;
will inject the SimpleGreeting implementation. However
@Inject @Any Greeting greeting;
will throw an "ambiguous dependency" error because now there are two implementations with that qualifier. The right implementation can be picked by specifying the additional qualifier such as:
@Inject @Any @Default Greeting greeting;
will inject the SimpleGreeting implementation and
@Inject @Any @Fancy Greeting greeting;
will inject the FancyGreeting implementation. The following injection will work anyway:
@Inject @Fancy Greeting greeting;
Lastly
@Inject @Default @Fancy Greeting greeting;
will give an "unsatisfied dependency" error because any bean with an explicit qualifier, except @Named, does not have the @Default qualifier.
@Any may also be used to add qualifiers programmatically such as:
@Inject @Any Instance<Greeting> greeting;
greeting.select(new AnnotationLiteral<Fancy>(){}).get();
will return a Greeting implementation with @Fancy qualifier. Here the "javax.enterprise.inject.Instance" and "javax.enteprise.util.AnnotationLiteral" classes are defined by the CDI spec.
@Any may also be used to iterate over all Greeting implementations such as:
@Inject @Any Instance<Greeting> greeting;
for (Greeting g : greeting) {
    // do something with g
}
The @New qualifier allows to obtain a depdendent object of a specified class, independent of the declared scope. So if SimpleGreeting is defined as:
@RequestScoped
public class SimpleGreeting implements Greeting {
    . . .
}
and injected as:
@Inject Greeting greeting;
then a request-scoped Greeting implementation (SimpleGreeting in this case) is injected. However if it is injected as:
@Inject @New Greeting greeting;
then the injected SimpleGreeting is in the @Dependent scope and only has @New qualifier (neither @Default or @Any).

No comments :

Post a Comment