When do custom qualifiers need to be used?
CDI custom qualifiers only need to be used when: (a) your (non-annotation) code has deliberately introduced ambiguity in the type to use; (b) you wish to inject a more specific type of object than indicated by the code; (c) you wish to have more flexible type selection and configurability than provided by the predefined qualifier @Named.
Qualifiers disambiguate which type to be created/injected by CDI.
(a) & (b) occur when you use high-level types in code to give a powerful general algorithm that can be reused and flexibly adjusted. E.g. often it is possible and encouraged to code an entire algorithm against an interface rather than a specific class - such as algorithms against List, Map or Queue. But often these algorithms only work well if we commit to particular implementations of the interface such as a SortedList, TreeMap or PriorityQueue. If CDI is to act as the object factory, carrying out object lifecycle management, initialising the correct objects for injection and dependency management, then it needs to know the full details.
Custom Qualifiers are smart and configurable in how they disambiguate the type to be created/injected by CDI. @Named is a much blunter instrument. For (c), when I mention "type selection" I mean that the developer can combine multiple qualifiers together to "logically select" the most appropriate type, without naming the precise class. @Named effectively requires nomination of the precise class - multiple qualifiers will allow CDI to work it out for you. Configurability refers to the ability to change behaviour of CDI at deployment time by modifying beans.xml (no need to modify code/annotations). i.e. can logically tune what CDI is doing and can do it all the way up to deployment time - not touching code OR annotations.
When do custom qualifiers need to be declared?
CDI custom qualifiers don't need to be created for every individual type that can be injected. This is most important.
Multiple qualifiers can be used on both the injection point and on type declarations. CDI matches the bean type plus the SET OF qualifiers when determining what type to inject.
To reduce the number of qualifiers to declare, it is a good idea to factor types into a number of attributes/concerns describing them. Then instead of one qualifier per type, can have one qualifier per type "attribute" - even if such an attribute is multi-valued such as Hash/Tree/Enum (see next paragraph).
Secondly, qualifiers should use parameters intelligently to further reduce the number of declarations required. Rather than creating 10 qualifiers you could have one qualifier which has a parameter that is a string or, preferably, an enum that can take on 10 values.
Combininge these two ideas (qualifiers=type attribute PLUS use qualifier parameters) into an example for a collections library: The types could be described with attributes represpenting Sorted/Non-Sorted? Hash/Tree/Array? Linked/Unlinked? If I specify a variable to be
@Inject @Sorted @Organisation("hash") @Navigation("linked") Map myMap;
then I have strong type selection because each of these aspects is guaranteed to be satisfied, I have a sorted linked hash map without knowing that this needs a very particular class in a particular package.
Points not been addressed yet:
If I use some annotation with parameterized bean name, then how it is actually type safe?
Strong typing is guaranteed because there must be a complete match with:
- the bean type
- the set of qualifiers
- the set of parameter values
No injected type can violate any of these specified things. Instead of thinking of typesafety as matching of a single string (packagename.classname) think of it as matching of multiple strings/values totally controlled by the developer - these same strings/values are used on both the declaration & injection side giving safe matching. Also, instead of thinking that you must match to the lowest level concrete class - remember that you can match to higher level classes in the inheritance hierarchy and smart use of @Default qualifiers on your types (remember Java EE 6 "smart defaults over configuration"?) will take to you to the preferred concrete types.
If I use custom qualifier for type safety per service or dao (per interface), then for a large sized application like having 1000 or more service or dao classes with multiple implementations, it will be messy. Then for large sized applications, Is that feasible to use type safe injection?
1000 or more service or dao classes? Really??! That would often flag a big design problem right away. Unless this is one super-mega-sized-guiness-records-attempt application for a complex business such as the tax department or NASA. Even then, it would be normal to break down into smaller Java EE modules / SOA services with much smaller number of classes. If this is what you have, you might want to look at simplifying your design - you might have far bigger problems then cut-and-past qualifier definitions...
Qualifiers are only needed where there is type ambiguity - i.e. lots of ancestor/descendant classes in the same type hierarchy. It is often the case that relatively few service or DAO classes are ambiguous.
As described above, don't use the one-qualifier-per-implementation-class pattern: instead refactor to use qualifier-per-type-aspect and also use descriptive parameters-per-qualifier implmentation patterns
It is feasible to use type safe injection with large applications.
If the answer of the above question is "No" then, what is the point to use type safety?
- the answer is "Yes"
- the scenario proposed (1000+ service/DAO classes) is very rare
Even if it is feasible to write annotations for type safety, in large applications, is it really worth the effort for just avoiding verbose xml configuration?
The purpose of CDI is not just to "avoid verbose xml configuration"
CDI has the following goals:
- Support for scopes (including new web conversation scope) with lifecycle management of objects
- Typesafe dependency injection mechanism, including the ability to select dependencies at either development or deployment time, without verbose configuration
- Support for Java EE modularity and the Java EE component architecture—the modular structure of a Java EE application is taken into account when resolving dependencies between Java EE components
- Integration with the Unified Expression Language (EL), allowing any contextual object to be used directly within a JSF or JSP page
- The ability to decorate injected objects
- The ability to associate interceptors to objects via typesafe interceptor bindings
- An event notification model
- A web conversation context in addition to the three standard web contexts defined by the Java Servlets specification
- An SPI allowing portable extensions to integrate cleanly with the container
This is WAY different and more useful than a basic XML config file turned into annotations - even the earlier basic Java EE 5 resource injection annotations were different & more useful than XML config turned into annotations.