CDI Dependency Injection - Tutorial II - Annotation Processing and Plugins - Java EE
Now to test how the Instance.isUnsatisfied by commenting out the implements ATMTransport in StandardAtmTransport class definition. You are essentially taking StandardAtmTransport out of the pool of possible injection of ATMTransport. There are no more defaults configured so it should be an unsatisfied.
Code Listing: StandardAtmTransport commenting out implements ATMTransport so Instance.isUnsatisfied returns true
package org.cdi.advocacy;
import javax.enterprise.inject.Default;
@Default
public class StandardAtmTransport { //implements ATMTransport {
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Standard transport");
}
}
Now the output is this:
picked JSON deposit called communicating with bank via JSON REST transport
Reread this section if you must and make sure you understand why you get the above output.
You can use Instance to load more than one bean as mentioned earlier. Let's lookup all installed installed @Default transports. To setup this example remove all of the annotations in the ATMTransport interfaces and make the beans.xml empty again (so no Alternative is active).
Code Listing: SoapAtmTransport making it @Default by removing @Soap qualifier
package org.cdi.advocacy;
//import javax.enterprise.inject.Alternative;
//@Soap
public class SoapAtmTransport implements ATMTransport {
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Soap transport");
}
}
Code Listing: JsonRestAtmTransport making it @Default by removing @Json qualifier
package org.cdi.advocacy;
//import javax.enterprise.inject.Alternative;
//@Alternative @Json
public class JsonRestAtmTransport implements ATMTransport {
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via JSON REST transport");
}
}
Code Listing: StandardAtmTransport making it @Default by removing any qualifiers from it
package org.cdi.advocacy;
//Just make sure there are no qualifiers
public class StandardAtmTransport implements ATMTransport {
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Standard transport");
}
}
We also need to make sure that the beans.xml file is empty.
Code Listing: {classpath}/META-INF/beans.xml removing all alternatives
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> </beans>Now use every transport that is installed using the annotation.
package org.cdi.advocacy;
import java.math.BigDecimal;
import java.util.Iterator;
import javax.annotation.PostConstruct;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;
@Named("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
@Inject
private Instance<ATMTransport> allTransports;
@PostConstruct
protected void init() {
System.out.println("Is this ambiguous? " + allTransports.isAmbiguous() );
System.out.println("Is this unsatisfied? " + allTransports.isUnsatisfied() );
}
public void deposit(BigDecimal bd) {
System.out.println("deposit called");
for (ATMTransport transport : this.allTransports) {
transport.communicateWithBank(null);
}
}
public void withdraw(BigDecimal bd) {
System.out.println("withdraw called");
for (ATMTransport transport : this.allTransports) {
transport.communicateWithBank(null);
}
}
}
In this context ambiguous means more than one. Therefore, CDI found more than one possibility for injection if ambiguous returns true. It should find three defaults.
Your output should look like this (or something close to this).
OutputIs this ambiguous? true Is this unsatisfied? false deposit called communicating with bank via JSON REST transport communicating with bank via Soap transport communicating with bank via Standard transport communicating with bank via the Super Fast transport
Note that we changed deposit to iterate through the available instances.
Now try something new comment out the implements ATMTransports in SuperFastAtmTransport, JsonRestAtmTransport and SoapRestAtmTransport. JsonRestAtmTransport and SoapRestAtmTransport transport class definition should have this //implements ATMTransport {.
Now rerun the example. You get this output.
OutputIs this ambiguous? false Is this unsatisfied? false deposit called communicating with bank via Standard transport
Since the only transport left is the standard transport (StandardAtmTransport), only it is in the output. The Instance is no longer ambiguous, there is only one so it prints false. CDI finds the one so it is not unsatisfied.
Now comment out all of //implements ATMTransport, and you get this:Is this ambiguous? false Is this unsatisfied? true deposit calledContinue 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