First I should clarify that this post is not intended to criticize CDI, but to discover the thinking and assumptions behind the design of CDI and that will have obvious influence on designing any web app, which uses CDI.
One of the most distinguished feature of CDI (of Java EE 6) is type safety. Jboss Seam was not safe in type. It uses name to qualify any instance to inject. Like bellow:
@Name("myBean")
public class MyBean implements Bean {
...
}
@Name("yourBean")
public class YourBean implements Bean {
...
}
While injecting MyBean one can do this:
@In
private Bean myBean; //myBean is injected
@In
private Bean yourBean; //yourBean is injected
And earlier versions of Spring (before 3.0), this type of injection happened like bellow:
Just define the beans in bean configuration file:
<bean id="myBean" class="com.example.common.MyBean">
...
</bean>
<bean id="yourBean" class="com.example.common.YourBean">
...
</bean>
And use named qualifier, deciding which one to use:
@Autowired
@Qualifier("myBean")
private Bean bean;
@Autowired
@Qualifier("yourBean")
private Bean bean;
But now in CDI, First you need to define a custom Qualifier
annotation for any specific type of object. Then use that annotation for qualifying that object. At the end of the day, when you look at your source code, you see that, you wasted considerable amount of time to write lots of custom annotations for dependency injection. Java community is moving towards annotations, leaving XML based configurations (verbose XML) behind. Is there anything that would convince anyone to think this (type safety with custom annotations) not as verbose annotations, but as an excellent and distinguished feature of CDI?
Edit:
Points, pushed to be highlighted
Short discussion on the above points
@Name
as the qualifier. So yes, in a large sized application it is feasible to use type safety when it is actually needed.Thanks and happy coding!
If you have multiple implementations of an interface, then ask yourself - do you need to differentiate between them after deployment?
If the answer is yes, then you still don't need qualifiers. Here's why:
To use your own example:
public class MyBean implements Bean {
...
}
public class YourBean implements Bean {
...
}
Then, you simply do:
@Inject MyBean bean;
or
@Inject YourBean bean;
If you don't like your instance variables to be of a concrete type and would rather see an interface, than do this:
private Bean bean;
@Inject
public void setBean(MyBean bean) {
this.bean = bean;
}
or
private Bean bean;
@Inject
public void setBean(YourBean bean) {
this.bean = bean;
}
In all the above cases it's completely qualifier free, absolutely type-safe, and definitely not verbose.
Then, to elaborate on the last point - does the developer need to chose the appropriate implementation or can the choice be made problematically?
If the the choice can be made problematically, then use a producer:
@Produces
public Bean obtainTheAppropriateBean(InjectionPoint ip) {
if (meetsConditionA(ip)) {
return getBeanImplA();
} else if (meetsConditionB(ip)) {
return getBeanImplB();
} else if (...) {
...
} else {
return getDefaultBeanImpl();
}
}
Still qualifier free, type-safe, and maybe still not verbose (trade choice for automation).
See this article for an excellent expansion of this point and ideas on how to use the InjectionPoint API.
I can see this question arising after the above examples. The answer is yes and here's a non-exhaustive list of reasons why you may chose to use them:
Imagine a situation where you have multiple interfaces and multiple implementations. Particular implementations may have common distinguishable traits and those traits are common to all of your interfaces. As an example, lets take the Java Colections Framework, specifically the List, Set, and Map interfaces. Each of those have multiple implementations, but there are common traits across all or some interfaces. For example linked nodes (fast iteration) - think LinkedList, LinkedHashSet, LinkedHashMap; sorted (ordering) - think TreeSet, TreeMap; hash-table based (fast insertion / removal / contains) - think HashSet, LinkedHashSet, HashMap, LinkedHashMap; concurent; random access; etc. Now, you could define the @Linked
, @Sorted
, and @Hash
annotations. Then inject:
@Inject @Linked @Hash private Map map;
@Inject @Sorted private Map map;
@Inject @Hash private Set set;
@Inject @Hash private Set set;
Now, is it worth doing this for a collections framework? I wouldn't do it, but I have a case similar to what I describe here in my current project at work (sorry, can't discuss).
And finally, you could use qualifiers to pass parameters to producers in conjunction with @Nonbinding
. Continuing with the collections framework above, define:
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Hash {
@Nonbinding int capacity() default 16;
@Nonbinding float loadFactor() default 0.75f;
}
This way, you could pass in a desired capacity and load factor to the producer returning anything hash table based like so:
@Inject @Hash(capacity = 256, loadFactor = 0.85f) private Set set;
@Inject @Hash private Set set;
@Inject @Hash(capacity = 8, loadFactor = 0.65f) private Map map;
I hope this answers your question. It sure is part of why I love CDI.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With