CDI Dependency Injection - Tutorial II - Annotation Processing and Plugins - Java EE
CDI provides a pluggable architecture allowing you to easily process your own annotations. Read this article to understand the inner workings of CDI and why this JSR is so important.
CDI simplifies and sanitizes the API for DI and AOP like JPA did for ORMs. Through its use of Instance and @Produces, CDI provides a pluggable architecture. This is a jaw dropping killer feature of CDI. Master this and you start to tap into the power of CDI. The last article was just to lay the ground work to the uninitiated for this article.
This article continues our tutorial of dependency injection with CDI.
This article covers:
- How to process annotations for configuration (injection level and class level)
- How to use an annotation for both injection and configuration (@Nonbinding)
- Using Instance to manage instances of possible injection targets
- CDI's plugin architecture for the masses
With this pluggable architecture you can write code that finds new dependencies dynamically. CDI can be a framework to write frameworks. This is why it is so important that CDI was led through the JSR process.
Just like last time, there are some instructions on how to run the examples: Source code for this tutorial, and instructions for use. A programming article without working sample code is like a sandwich with no condiments or dry toast without jelly.
Advanced CDI tutorial
The faint of heart stop here. All of the folks who want to understand the inner workings of CDI continue. So far, we have been at the shallow, warm end of the pool. Things are about to get a little deeper and colder. If you need to master CDI, then this article if for you. If you don't know what CDI is then read the first CDI DI article.
Advanced: Using @Produces and InjectionPoint to create configuration annotations
Our ultimate goal is to define an annotation that we can use to configure the retry count on a transport. Essentially, we want to pass a retry count to the transport.
We want something that looks like this:Code Listing: TransportConfig annotations that does configuration
@Inject @TransportConfig(retries=2) private ATMTransport transport;(This was my favorite section to write, because I wanted to know how to create a annotation configuration from the start.)
Before we do that we need to learn more about @Produces and InjectionPoints. We are going to use a producer to read information (meta-data) about an injection point. A major inflection point for learning how to deal with annotations is the InjectionPoints. The InjectionPoints has all the metadata we need to process configuration annotations.
An InjectionPoint is a class that has information about an injection point. You can learn things like what is being decorated, what the target injection type is, what the source injection type, what is the class of the owner of the member that is being injected and so forth.
Let's learn about passing an injection point to @Produces. Below I have rewritten our simple @Produces example from the previous article, except this time I pass an InjectionPoint argument into the mix.
Code Listing: TransportFactory getting meta-data about the injection point
package org.cdi.advocacy;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
public class TransportFactory {
@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {
System.out.println("annotated " + injectionPoint.getAnnotated());
System.out.println("bean " + injectionPoint.getBean());
System.out.println("member " + injectionPoint.getMember());
System.out.println("qualifiers " + injectionPoint.getQualifiers());
System.out.println("type " + injectionPoint.getType());
System.out.println("isDelegate " + injectionPoint.isDelegate());
System.out.println("isTransient " + injectionPoint.isTransient());
return new StandardAtmTransport();
}
}
Now we just run it and see what it produces.
The above produces this output.
Output
annotated AnnotatedFieldImpl[private org.cdi.advocacy.ATMTransport org.cdi.advocacy.AutomatedTellerMachineImpl.transport]
bean ManagedBeanImpl[AutomatedTellerMachineImpl, {@javax.inject.Named(value=atm), @Default(), @Any()}, name=atm]
member private org.cdi.advocacy.ATMTransport org.cdi.advocacy.AutomatedTellerMachineImpl.transport
qualifiers [@Default()]
type interface org.cdi.advocacy.ATMTransport
isDelegate false
isTransient false
deposit called
communicating with bank via Standard transport
It appears from the output that annotated tells us about the area of the program we annotated. It also appears that bean tells us which bean the injection is happening on.
From this output you can see that the annotated property on the injectionPoint has information about which language feature (field, constructor argument, method argument, etc.). In our case it is the field org.cdi.advocacy.AutomatedTellerMachineImpl.transport. is being used as the target of the injection, it is the thing that was annotated.
From this output you can see that the bean property of the injectionPoint is being used to describe the bean whose member is getting injected. In this case, it is the AutomatedTellerMachineImpl whose is getting the field injected.
I won't describe each property, but as an exercise you can.
Exercise: Look up the InjectionPoint in the API documentation. Find out what the other properties mean. How might you use this meta-data? Can you think of a use case or application where it might be useful? Send me your answers on the CDI group mailing list. The first one to send gets put on the CDI wall of fame. (All others get honorable mentions.)
Drilling further you can see what is in the beans and annotated properties.
Code Listing: TransportFactory.createTransport drilling further into the meta-data about the injection point
@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {
System.out.println("annotated " + injectionPoint.getAnnotated());
System.out.println("bean " + injectionPoint.getBean());
System.out.println("member " + injectionPoint.getMember());
System.out.println("qualifiers " + injectionPoint.getQualifiers());
System.out.println("type " + injectionPoint.getType());
System.out.println("isDelegate " + injectionPoint.isDelegate());
System.out.println("isTransient " + injectionPoint.isTransient());
Bean<?> bean = injectionPoint.getBean();
System.out.println("bean.beanClass " + bean.getBeanClass());
System.out.println("bean.injectionPoints " + bean.getInjectionPoints());
System.out.println("bean.name " + bean.getName());
System.out.println("bean.qualifiers " + bean.getQualifiers());
System.out.println("bean.scope " + bean.getScope());
System.out.println("bean.stereotypes " + bean.getStereotypes());
System.out.println("bean.types " + bean.getTypes());
Annotated annotated = injectionPoint.getAnnotated();
System.out.println("annotated.annotations " + annotated.getAnnotations());
System.out.println("annotated.annotations " + annotated.getBaseType());
System.out.println("annotated.typeClosure " + annotated.getTypeClosure());
return new StandardAtmTransport();
}
Now we are cooking with oil. Throw some gas on that flame. Look at the wealth of information that the InjectionPoint defines.
Output... bean.beanClass class org.cdi.advocacy.AutomatedTellerMachineImpl bean.injectionPoints [InjectionPointImpl[private org.cdi.advocacy.ATMTransport org.cdi.advocacy.AutomatedTellerMachineImpl.transport]] bean.name atm bean.qualifiers [@javax.inject.Named(value=atm), @Default(), @Any()] bean.scope interface javax.enterprise.context.Dependent bean.stereotypes [] bean.types [class org.cdi.advocacy.AutomatedTellerMachineImpl, interface org.cdi.advocacy.AutomatedTellerMachine, class java.lang.Object] annotated.annotations AnnotationSet[@javax.inject.Inject()] annotated.annotations interface org.cdi.advocacy.ATMTransport annotated.typeClosure [interface org.cdi.advocacy.ATMTransport, class java.lang.Object] ...
We see that bean.beanClass gives up the class of the bean that is getting the injected field. Remember that one, we will use it later.
We can see that bean.qualifiers gives up the list of qualifiers for the AutomatedTellerMachineImpl.
We can also see that annotated.annotations gives us the list of annotations that are associated with the injected field. We will use this later to pull the configuration annotation and configure the transport with it.
Exercise: Look up the Bean and Annotated in the API documentation. Find out what the other properties mean. How might you use this meta-data? Can you think of a use case or application where it might be useful? Send me your answers on the CDI group mailing list. The first one to send gets put on the CDI wall of fame. (All others get honorable mentions.)
Ok now that we armed with an idea of what an Injection point is. Let's get configuring our transport.
First let's define an TransportConfig annotation. This is just a plain runtime annotation as follows:
Code Listing: TransportConfig an annotation used for configuration
package org.cdi.advocacy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface TransportConfig {
int retries() default 5;
}
Notice that this annotation has one member retries, which we will use to configure the ATMTransport (transport).
Now go ahead and use this to decorate the injection point as follows:
Code Listing: AutomatedTellerMachineImpl using TransportConfig to configure retries
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
@Inject @TransportConfig(retries=2)
private ATMTransport transport;
Once it is configured when you run it, you will see the following output from our producer:
Outputannotated.annotations AnnotationSet[@javax.inject.Inject(), @org.cdi.advocacy.TransportConfig(retries=2)]
This means the annotation data is there. We just need to grab it and use it. Stop and ponder on this a bit. This is pretty cool. The producer allows me to customize how annotations are consumed. This is powerful stuff and one of the many extension points available to CDI. CDI was meant to be extensible. It is the first mainstream framework that encourages you to consume your own annotation data. This not some obscure framework feature. This is in the main usage.
Please recall that the injectionPoint.annotated.annotations gives us the list of annotations that are associated with the injected field, namely, the transport field of the AutomatedTellerMachineImpl. Now we can use this to pull the configuration annotation and configure the transport with it. The party is rolling now.
Now we need to change the transport implementations to handle setting retires. Since this is an example, I will do this simply by adding a new setter method for retires (setRetries) to the ATMTranport interface like so:
Code Listing: ATMTransport adding a retries property
package org.cdi.advocacy;
public interface ATMTransport {
public void communicateWithBank(byte[] datapacket);
public void setRetries(int retries);
}
Then we need to change each of the transports to handle this new retries property as follows:
Code Listing: StandardAtmTransport adding a retries property
package org.cdi.advocacy;
public class StandardAtmTransport implements ATMTransport {
private int retries;
public void setRetries(int retries) {
this.retries = retries;
}
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Standard transport retries=" + retries);
}
}
Continue reading... Click on the navigation links below the author bio to read the other pages of this article.
Be sure to check out part I of this series as well: CDI DI Tutorial!
About the author
This article was written with CDI advocacy in mind by Rick Hightower with some collaboration from others. Rick Hightower has worked as a CTO, Director of Development and a Developer for the last 20 years. He has been involved with J2EE since its inception. He worked at an EJB container company in 1999. He has been working with Java since 1996, and writing code professionally since 1990. Rick was an early Spring enthusiast. Rick enjoys bouncing back and forth between C, Python, Groovy and Java development.
Although not a fan of EJB 3, Rick is a big fan of the potential of CDI and thinks that EJB 3.1 has come a lot closer to the mark.
Rick Hightower is CTO of Mammatus and is an expert on Java and Cloud Computing. Rick is invovled in Java CDI advocacy and Java EE. CDI Implementations - Resin Candi - Seam Weld - Apache OpenWebBeans
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Rogerio Liesenfeld replied on Tue, 2011/04/05 - 11:18am
Unless I missed something, though, CDI does not (yet?) support injection into non-managed beans, as provided through @Configurable in Spring. Is this planned? And if it is, with build-time or load-time bytecode modification, or with either mechanism as in Spring?
Frank Schwarz replied on Tue, 2011/04/05 - 12:36pm
Rick Hightower replied on Tue, 2011/04/05 - 2:58pm
in response to:
Rogerio Liesenfeld
All of the examples were tested with Resin CANDI and WELD.
I reserve the right to be wrong, but I am pretty sure I am right.
Rick Hightower replied on Tue, 2011/04/05 - 2:59pm
in response to:
Frank Schwarz
Rick Hightower replied on Tue, 2011/04/05 - 5:35pm
in response to:
Rick Hightower
The example would not work if you "new"ed AutomatedTellerMachineImpl instead of looking it up.
Dan Allen replied on Tue, 2011/04/05 - 9:12pm
You can also add beans programmatically using a CDI extension. Observe the BeforeBeanDiscovery event and use the addAnnotatedType method:
event.addAnnotatedType(beanManager.createAnnotatedType(MyClass.class));That's one idea. We were using this in Solder for a while, but we opted to switch back to using beans.xml.
Samuli Saarinen replied on Wed, 2011/04/06 - 12:24am
Hi,
Is the something missing from the last example code snippet or how this can work with identical annotation literals?
standardTransport = allTransports.select(new AnnotationLiteral(){}).get(); jsonTransport = allTransports.select(new AnnotationLiteral(){}).get(); soapTransport = allTransports.select(new AnnotationLiteral(){}).get();Rogerio Liesenfeld replied on Wed, 2011/04/06 - 10:18am
in response to:
Rick Hightower
But suppose I don't want to depend on a "ServiceLocator" lookup in top-level components, ie, I don't want to have to make calls like
AutomatedTellerMachine atm = BeanContainerManager.getInstance().getBeanByType(AutomatedTellerMachine.class);
in AtmMain.java. Instead, I would prefer to simply write
AutomatedTellerMachine atm = new AutomatedTellerMachineImpl();
BTW, this "Impl" class would probably be the only implementation in practice, so in most cases it would make more sense to not have the separate interface.
Personally, I prefer my business service classes to have initial state passed through a constructor. With injection into non-managed beans, it would be possible to use custom constructors and final instance fields, while still having dependencies injected into instances created with "new".
Rick Hightower replied on Wed, 2011/04/06 - 12:52pm
in response to:
Samuli Saarinen
Good eye. It appears I have a slight error in my code listing. DOH! I will fix it and repost that last listing. I meant to pass the annotation to annotation literal and then to the select. It appears I misfired. :)
Pobody's Nerfect.
Rick Hightower replied on Wed, 2011/04/06 - 7:12pm
in response to:
Samuli Saarinen
Rick Hightower replied on Wed, 2011/04/06 - 7:19pm
in response to:
Frank Schwarz
Rick Hightower replied on Wed, 2011/04/06 - 8:02pm
in response to:
Rogerio Liesenfeld
"There is no standards-defined way of injecting into objects instantiated via 'new' in CDI as you would do in the case of Spring @Configurable."
"It is doubtful whether there will ever be. The issue is that it would basically require mandating that all CDI implementations use AspectJ style byte-code manipulation as opposed to code generation, proxying, etc."
"If this is something that is really needed, you could create an AspectJ based tool for CDI that takes a class definition and adds CDI run-time look-ups behind the scenes to mimic injection/other CDI functionality. If run-time weaving is necessary, you would then need to specify a weaver for class-loading. In my opinion, this is not really something that needs to be done (as part of the JSR) since it adds additional build/compile/class-loading complexity for a very rare, domain-driven-design purist case. For example, I know of no Spring shops that use @Configurable or compile time weaving and a very small handful that uses load-time weaving."
I could see us adding something like this to CDISource.
Mark Struberg replied on Thu, 2011/04/07 - 12:50am
Hi Rick!
Good series! I just like to add a few oftern overlooked gems. Today it's the @Typed Annotation. It can be used to explicitely declare the type of a bean. There are 2 cases where this is reaaaly handy.
Use case 1: 'disabling' a java class as bean
Let's say we have a
and we have a producer method like
A Menu is naturally a tree with a 1:n parent menu relationship. So this will just be created via an @ApplicationScoped MenuServiceImpl or such - but never directly as a bean! Annotate the Menu class as @Typed() effectively says: this bean has no Type at all, thus it will not satisfy any injection point. This has almost the same effect as veto() ing a bean in an Extension (via @Observes ProcessAnnotatedType) but without needing to write an Extension. If we wouldn't annotate our Menu class as @Typed(), it would get picked up as @Dependent scoped bean automatically (I personally don't like that, but thats what the spec defines) - leading to an AmbiguousResolutionException.
The 2nd case where @Typed is really handy is if you have subclasses which should not get picked up as the base type:
If I would not use the @Typed annotation at the MenuAdminService, then it would automatically satisfy all the types in it's whole class hierarchy - including the MenuService it extends. So if you would try to
then you would get an AmbiguousResolutionException (remember: both MenuService and MenuAdminService implement the MenuService type!). By adding
it will only act as the type in the annotation and not automatically use all the parent types too!
Rick Hightower replied on Thu, 2011/04/07 - 10:25am
in response to:
Mark Struberg
Mark Struberg replied on Thu, 2011/04/07 - 11:47am
Might as well add another tip: Beware of the Constructor!
Writing CDI bean initialisation code in Constructors is not a good thingy as you will see weird effects.
Remember - all beans you get injected (except @Dependent ones) are always 'contextual references' = proxies for their underlying contextual instances! But CDI implementations do not use Java's own reflect proxy (because this one is only able to proxy interfaces) but must use instance proxies.
How goes? The CDI implementations I know (Weld, CanDI and of course OpenWebBeans) create proxies by dynamically subclassing the class which should get proxied. So whenever a proxy gets created, it will naturally also invoke the constructors up in the parent-class chain.
Thus writing the classic reference counter for your bean will give you wrong numbers:
@SessionScoped public class MyBean implements Serializable { private static int refCounter = 0; public MyBean() { refCounter++; // <- this will be called too often since the ct will also get called for proxies } ... }Instead use the @PostConstruct or @Inject annotations on any method:
@SessionScoped public class MyBean implements Serializable { private static int refCounter = 0; @Inject public void init() { refCounter++; // <- this will be called too often since the ct will also get called for proxies } @PostConstruct pubic void anotherInit() { } ... @PreDestroy // ofc this works as well btw! public void cleanup() { } }Mark Struberg replied on Thu, 2011/04/07 - 12:19pm
@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {please note that this would only work for producing @Dependent scoped beans! (which is exactly what you get if you don't explicitely specify another scope).
The reason why this only works with @Dependent scoped beans is that for all NormalScoped beans (@RequestScoped, @SessionScoped, @ApplicationScoped,...), you just don't know where they get injected into.
In fact, your contextual instance (your one-and-only instance of that bean in the given scope) will never get @Inject-ed directly, but instead you will always 'only' get a contextal reference (the proxy) injected into your injection point!
Ergo, this trick only works for @Dependent scoped beans...
Overally the JSR-299 spec is one of the best specs I've ever read. But as I already expressed above: I don't like the automatic pickup of not-explicitely scoped beans as @Dependent.
Reason is that this information is not really obvious and often gets forgotten. If you forget about adding the scope annotation or per accident import e.g. javax.faces.beans.SessionScoped instead of javax.enterprise.context.SessionScoped - IDEs are damn good in picking up the wrong import ;) - you will end up with @Dependent. This mean you will be creating a new instance for each and every injection point and EL invocation - and loosing lots of data in between...
Rogerio Liesenfeld replied on Thu, 2011/04/07 - 12:44pm
in response to:
Rick Hightower
Use of java.lang.instrument is much better and simpler than using AspectJ or compile-time instrumentation. And by using the Attach API, there is no need to have users manually add a "-javaagent:instrumentation.jar" JVM parameter when running an app/container. There is some runtime overhead, obviously, but I believe it should be small enough (based on my experience with the JMockit tools).
I wish CDISource the best of luck. CDI is great, and I am sure it will only improve.
Mark Struberg replied on Thu, 2011/04/07 - 12:58pm
@Rogerio:
I fear using -javaagent has one BIG problem: you will always hit the SystemClassLoader!
This is really a no-go in EE server scenarios and any other situations where you need classpath isolation by using a ClassLoaders hierarchy. The whole javaagent approach is imo only useful if you have a standalone application.In all other situations you will most probably either break the application - or even worse - open a fat security hole without even notizing it!
All existing CDI implementations (Dan and Reza can confirm this for Weld and CanDI) are using a ClassLoader hack. So we are not using compile time instrumentation - in fact we don't change the original bean classes at all! We just additionally create proxies for them at runtime by 'dynamically' creating subclasses via in memory byte-code engineering.So you can still write code like
Mark Struberg replied on Thu, 2011/04/07 - 1:12pm
in response to:
Rogerio Liesenfeld
> support injection into non-managed beans
Stu (Stuart Douglas) of JBoss fame wrote a portable CDI Extension for exactly that [1]. This Extension basically will dynamically apply annotations to your classes - without having to write them in your code. It will give you the kind of XML config you might know from Spring 2.
In general: if you are looking for CDI Extensions, then check JBoss Seam3 [2] and Apache MyFaces CODI [3] first
LieGrue,
strub
[1] http://docs.jboss.org/seam/3/xml-config/latest/reference/en-US/html_single/
[2] http://docs.jboss.org/seam/3/3.0.0.Final/reference/en-US/html/
[3] http://myfaces.apache.org/extensions/cdi/
PS: <shamelessselfadvert>the Seam3 doc is much better, but CODI is in some areas much more powerful ;) </shamelessselfadvert>
Rick Hightower replied on Thu, 2011/04/07 - 1:53pm
in response to:
Mark Struberg
When I think of successful OS projects, they usually have on thing in common: good docs.
Facelets was well documented and easy to learn. Hibernate had excellent documents (not everyone agrees with this). Spring has excellent documents. etc. etc.
Docs are a good sign of the overall quality of a project. That being said. I am horrible at writing docs (see crank).
Reza Rahman replied on Thu, 2011/04/07 - 2:29pm
Rogerio Liesenfeld replied on Thu, 2011/04/07 - 3:26pm
in response to:
Mark Struberg
Actually, a ClassFileTransformer (registered through the java.lang.instrument.Instrumentation object provided by a "Java agent") can transform classes loaded by *any* classloader. In fact, the first parameter to the "transform" method is the ClassLoader which defined the class to be transformed. Therefore, the transformer implementation can decide whether to transform or not each class based on its defining classloader.
Also, it's not actually necessary to use the "-javaagent" parameter at all. A Java agent can be loaded programmaticaly (at least from JDK 1.6 on).
I have actually implemented this kind of stuff in a tool which uses multiple classloaders; it works.
Mark Struberg replied on Fri, 2011/04/08 - 3:59am
in response to:
Rogerio Liesenfeld
Hi Rogerio!
What I meant is that by using -javaagent alll the classes you need for the transformation will end up getting loaded via the SystemClassLoader. I'm not 100% sure yet about the 'transformed classes' butI think they also get loaded that way.
You will see this effect if you use those libs with tomcat - they will just refuse to work. The reason is that tomcat implements a security feature to prevent people from overloading classes which got loaded via the SystemClassLoader. I stumbled accross this problem in OpenJPA:
https://issues.apache.org/jira/browse/OPENJPA-1454
https://issues.apache.org/jira/browse/OPENJPA-1410
I haven't yet used the programmatical way to use the javaagent in Java6 of course
Jessie Mear replied on Wed, 2011/09/07 - 6:40am
Rick Siskey replied on Thu, 2011/10/27 - 12:06am
Krishna Bheeman... replied on Sun, 2012/06/24 - 9:49pm
Pradeep Kumar replied on Wed, 2012/09/19 - 12:57pm