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 ofInstance 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 thefirst 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
Code Listing: TransportConfig annotations that does configuration
view source
print?
1.@Inject @TransportConfig(retries=2)
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
view source
print?
01.package org.cdi.advocacy;
02.
03.import javax.enterprise.inject.Produces;
04.import javax.enterprise.inject.spi.InjectionPoint;
05.
06.public class TransportFactory {
07.
08.
09.@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {
10.
11.System.out.println("annotated
" + injectionPoint.getAnnotated());
12.System.out.println("bean
" + injectionPoint.getBean());
13.System.out.println("member
" + injectionPoint.getMember());
14.System.out.println("qualifiers
" + injectionPoint.getQualifiers());
15.System.out.println("type
" + injectionPoint.getType());
16.System.out.println("isDelegate
" + injectionPoint.isDelegate());
17.System.out.println("isTransient
" + injectionPoint.isTransient());
18.
19.return new StandardAtmTransport();
20.}
21.
22.}
Now we just run it and see what it produces. The
above produces this output. Output
view source
print?
01.annotated
AnnotatedFieldImpl[private org.cdi.advocacy.ATMTransport
org.cdi.advocacy.AutomatedTellerMachineImpl.transport]
02.bean
ManagedBeanImpl[AutomatedTellerMachineImpl, {@javax.inject.Named(value=atm), @Default(), @Any()}, name=atm]
03.member private org.cdi.advocacy.ATMTransport
org.cdi.advocacy.AutomatedTellerMachineImpl.transport
04.qualifiers [@Default()]
05.type interface org.cdi.advocacy.ATMTransport
06.isDelegate false
07.isTransient false
08.deposit called
09.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 AutomatedTellerMachineImplwhose 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
view source
print?
01.@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {
02.
03.System.out.println("annotated
" + injectionPoint.getAnnotated());
04.System.out.println("bean
" + injectionPoint.getBean());
05.System.out.println("member
" + injectionPoint.getMember());
06.System.out.println("qualifiers
" + injectionPoint.getQualifiers());
07.System.out.println("type
" + injectionPoint.getType());
08.System.out.println("isDelegate
" + injectionPoint.isDelegate());
09.System.out.println("isTransient
" + injectionPoint.isTransient());
10.
11.Bean<?> bean =
injectionPoint.getBean();
12.
13.System.out.println("bean.beanClass
" + bean.getBeanClass());
14.System.out.println("bean.injectionPoints
" + bean.getInjectionPoints());
15.System.out.println("bean.name
" + bean.getName());
16.System.out.println("bean.qualifiers
" + bean.getQualifiers());
17.System.out.println("bean.scope
" + bean.getScope());
18.System.out.println("bean.stereotypes
" + bean.getStereotypes());
19.System.out.println("bean.types
" + bean.getTypes());
20.
21.Annotated annotated =
injectionPoint.getAnnotated();
22.System.out.println("annotated.annotations
" + annotated.getAnnotations());
23.System.out.println("annotated.annotations
" + annotated.getBaseType());
24.System.out.println("annotated.typeClosure
" + annotated.getTypeClosure());
25.
26.return new StandardAtmTransport();
27.}
Now we are cooking with oil. Throw
some gas on that flame. Look at the wealth of information that theInjectionPoint defines.
Output
view source
print?
01....
02.bean.beanClass class org.cdi.advocacy.AutomatedTellerMachineImpl
03.bean.injectionPoints
[InjectionPointImpl[privateorg.cdi.advocacy.ATMTransport
org.cdi.advocacy.AutomatedTellerMachineImpl.transport]]
04.bean.name atm
05.bean.qualifiers [@javax.inject.Named(value=atm), @Default(), @Any()]
06.bean.scope interface javax.enterprise.context.Dependent
07.bean.stereotypes []
08.bean.types [class org.cdi.advocacy.AutomatedTellerMachineImpl, interfaceorg.cdi.advocacy.AutomatedTellerMachine, class java.lang.Object]
09.annotated.annotations
AnnotationSet[@javax.inject.Inject()]
10.annotated.annotations interface org.cdi.advocacy.ATMTransport
11.annotated.typeClosure
[interface org.cdi.advocacy.ATMTransport, classjava.lang.Object]
12....
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
view source
print?
01.package org.cdi.advocacy;
02.
03.
04.import java.lang.annotation.Retention;
05.import java.lang.annotation.Target;
06.import static java.lang.annotation.ElementType.*;
07.import static java.lang.annotation.RetentionPolicy.*;
08.
09.
10.
11.@Retention(RUNTIME) @Target({TYPE, METHOD,
FIELD, PARAMETER})
12.public @interface TransportConfig {
13.int retries() default 5;
14.}
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
view source
print?
1.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
2.
3.@Inject @TransportConfig(retries=2)
4.private ATMTransport transport;
Once it is
configured when you run it, you will see the following output from our
producer:
Output
view source
print?
1.annotated.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 theATMTranport interface like so:
Code Listing: ATMTransport adding a retries property
view source
print?
1.package org.cdi.advocacy;
2.
3.public interface ATMTransport {
4.public void communicateWithBank(byte[] datapacket);
5.public void setRetries(int retries);
6.}
Then we need to change each of the
transports to handle this new retries property as follows:
Code Listing: StandardAtmTransport adding a retries property
view source
print?
01.package org.cdi.advocacy;
02.
03.public class StandardAtmTransport implements ATMTransport {
04.
05.private int retries;
06.
07.public void setRetries(int retries) {
08.this.retries = retries;
09.}
10.
11.
12.public void communicateWithBank(byte[] datapacket) {
13.System.out.println("communicating
with bank via Standard transport retries=" + retries);
14.}
15.
16.}
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
Now we just change
the producer to grab the new annotation and configure the transport as follows:
(For clarity I took out all of the Sysout.prinltns.)
Code Listing: TransportFactory using the annotation configuration to configure a new instance of the transport
view source
print?
01.package org.cdi.advocacy;
02.
03....
04.import javax.enterprise.inject.spi.Annotated;
05.import javax.enterprise.inject.spi.Bean;
06.import javax.enterprise.inject.spi.InjectionPoint;
07.
08.public class TransportFactory {
09.@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {
10.
11.Annotated annotated =
injectionPoint.getAnnotated();
12.
13.TransportConfig
transportConfig = annotated.getAnnotation(TransportConfig.class);
14.
15.
16.
17.StandardAtmTransport
transport = new StandardAtmTransport();
18.
19.transport.setRetries(transportConfig.retries());
20.return transport;
21.}
22.
23.}
(Side Note: we are
missing a null pointer check. The annotation configuration could be null if the
user did not set it, you may want to handle this. The example is kept
deliberately short.)
The code just gets
the annotation and shoves in the retires into the transport, and then just
returns the transport.
We now have a
producers that can use an annotation to configure an injection.
Here is our new
output:
Output
view source
print?
1....
2.deposit called
3.communicating with bank via Standard transport
retries=2
You can see our
retries are there as we configured them in the annotation. Wonderful!
Annotation processing for the masses!
Ok we are done with
this example. What remains is a victory lap. Let's say we had multiple
transports in a single ATM and you wanted to configure all of the outputs at
once.
Let's configure the transport based
on an annotation in the parent class of the injection target, namely,AutomatedTellerMachine.
Code Listing: TransportFactory using the annotation configuration from class not field to configure a new instance of the transport
view source
print?
01.public class TransportFactory {
02.@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {
03.
04.Bean<?> bean =
injectionPoint.getBean();
05.TransportConfig
transportConfig = bean.getBeanClass().getAnnotation(TransportConfig.class);
06.
07.StandardAtmTransport
transport = new StandardAtmTransport();
08.
09.transport.setRetries(transportConfig.retries());
10.return transport;
It is an exercise
for the reader to make the injection level annotation (from the last example)
override the class level annotations. As always, if you are playing along in
the home version of CDI hacker, send me your solution. Best solution gets my
admiration.
Output
view source
print?
1.deposit called
2.communicating with bank via Standard transport
retries=7
Exercise: Make the injection from the
field override the injection from the class. It is a mere matter of Java code.
Send me your solution on the CDI group mailing list. The first one to
send gets put on the CDI wall of fame. (All others get honorable mentions.)
Advanced Using
@Nonbinding to combine a configuration annotation and a qualifier annotation
into one annotation
In the section titled "Using
@Qualfiers with members to discriminate injection and stop the explosion of
annotation creation" we covered adding additional members
to a qualifier annotation and then in *"Advanced: Using @Produces and
InjectionPoint to create configuration annotations"* we talked about how
to write an annotation to configure an injection. Wouldn't be great if we could
combine these two concepts into one annotation?
The problem is that
qualifier members are used to do the discrimination. We need some qualifier
members that are not used for configuration not discrimination.
To make an qualifier member just a
configuration member use @Nonbinding annotation as follows:
Code Listing: Transport qualifier annotation using @Nonbinding to add configuration retries param
view source
print?
01.package org.cdi.advocacy;
02.
03.import java.lang.annotation.Retention;
04.import java.lang.annotation.Target;
05.import static java.lang.annotation.ElementType.*;
06.import static java.lang.annotation.RetentionPolicy.*;
07.
08.import javax.enterprise.util.Nonbinding;
09.import javax.inject.Qualifier;
10.
11.
12.@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
13.public @interface Transport {
14.TransportType type() default TransportType.STANDARD;
15.int priorityLevel() default -1;
16.String name() default "standard";
17.
18.@Nonbinding int retries() default 5;
19.
20.}
Now let's add the setRetries to the Fast
Transport:
Code Listing: Transport qualifier annotation using @Nonbinding to add configuration retries param
Code Listing: Transport qualifier annotation using @Nonbinding to add configuration retries param
view source
print?
01.package org.cdi.advocacy;
02.
03.@Transport(type=TransportType.STANDARD, priorityLevel=1, name="super")
04.public class SuperFastAtmTransport implements ATMTransport {
05.private int retries=0;
06.
07.public void setRetries(int retries) {
08.this.retries=retries;
09.}
10.
11.
12.public void communicateWithBank(byte[] datapacket) {
13.System.out.println("communicating
with bank via the Super Fast transport retries=" + retries);
14.}
15.
16.}
Then we use it as follows:
view source
print?
1.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
2.
3.@Inject @Transport(type=TransportType.STANDARD, priorityLevel=1, name="super", retries=9)
4.private ATMTransport
transport;
5....
Ouptut
view source
print?
1.deposit called
2.communicating with bank via Standard transport
retries=9
The final result is
we have one annotation that does both qualification and configuration. Booyah!
Exercise: There is an easter egg in
this example. There is concept we talked about earlier (in the qualifier
discrimination but never added. Please find it and describe it. What are some
potential problems of using this approach? 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.)
Advanced: Using
Instance to inject transports
The use of the class Instance allows you to dynamically look up instances of a certain type. This is
the plugin architecture for the masses, built right into CDI. Grok this and you
will not only understand CDI but have a powerful weapon in your arsenal of mass
programming productivity.
These instances can be instances that
are in a jar files. For example the AutomatedTellerMachinecould work with
transports that did not even exist when the AutomatedTellerMachine was created. If you don't grok that, read the last sentence again. You
are tapping into the scanning capabilities of CDI. This power is there for the
taking. The Instance class is one of the things that makes CDI so cool and flexible. In this
section, I hope to give it some justice while still keeping the example small
and understandable.
Let's say we wanted
to work with multiple transports. But we don't know which transport is
configured and on the classpath. It could be that the build was special for a
certain type of transport, and it just does not exist on the classpath. Suspend
disbelief for a moment and let's look at the code.
Code Listing: AutomatedTellerMachineImpl using Instance
view source
print?
01.package org.cdi.advocacy;
02.
03.import java.math.BigDecimal;
04.
05.import javax.annotation.PostConstruct;
06.import javax.enterprise.inject.Default;
07.import javax.enterprise.inject.Instance;
08.import javax.inject.Inject;
09.import javax.inject.Named;
10.
11.@Named("atm")
12.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
13.
14.@Inject @Soap
15.private Instance<ATMTransport> soapTransport;
16.
17.@Inject @Json
18.private Instance<ATMTransport> jsonTransport;
19.
20.@Inject @Default
21.private Instance<ATMTransport> defaultTransport;
22.
23.private ATMTransport transport;
24.
25.@PostConstruct
26.protected void init() {
27.if (!defaultTransport.isUnsatisfied()) {
28.System.out.println("picked
Default");
29.transport =
defaultTransport.iterator().next();
30.} else if (!jsonTransport.isUnsatisfied())
{
31.System.out.println("picked
JSON");
32.transport =
jsonTransport.iterator().next();
33.} else if (!soapTransport.isUnsatisfied())
{
34.System.out.println("picked
SOAP");
35.transport =
soapTransport.iterator().next();
36.}
37.}
Notice we are using *`Instance`* as
the field type instead of ATMTransport. Then we look up
the actual transport. We can query a Instance with the Instance.isUnsatisfied to see it this transport actually exist. There is an Instance.get method to retrieve a single transport, but I used
*`Instance.iterator().next()`* to highlight an important aspect of Instance, namely, it can
return more than one. For example, there could be 20 @Default based transports in the system.
Imagine if you were implementing a
chain of responsibility pattern or a command pattern, and you wanted an easy
way to discover the actions or commands that were on the classpath. Instance would be that way. CDI makes this type of plugin development very easy.
If it could find a single @Default, the one we have
been using since the start, on the classpath. The output from the above would
be as follows:
Output
view source
print?
1.picked Default
2.deposit called
3.communicating with bank via Standard transport
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
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 soInstance.isUnsatisfied returns true
view source
print?
01.package org.cdi.advocacy;
02.
03.import javax.enterprise.inject.Default;
04.
05.@Default
06.public class StandardAtmTransport { //implements ATMTransport {
07.
08.public void communicateWithBank(byte[] datapacket) {
09.System.out.println("communicating
with bank via Standard transport");
10.}
11.
12.}
Now the output is this:
view source
print?
1.picked JSON
2.deposit called
3.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 theATMTransport interfaces and make the beans.xml empty again (so no Alternative is active).
Code Listing: SoapAtmTransport making it @Default by removing @Soap qualifier
view source
print?
01.package org.cdi.advocacy;
02.
03.//import
javax.enterprise.inject.Alternative;
04.
05.//@Soap
06.public class SoapAtmTransport implements ATMTransport {
07.
08.public void communicateWithBank(byte[] datapacket) {
09.System.out.println("communicating
with bank via Soap transport");
10.}
11.
12.}
Code Listing: JsonRestAtmTransport making it @Default by removing @Json qualifier
view source
print?
01.package org.cdi.advocacy;
02.
03.//import
javax.enterprise.inject.Alternative;
04.
05.//@Alternative
@Json
06.public class JsonRestAtmTransport implements ATMTransport {
07.
08.public void communicateWithBank(byte[] datapacket) {
09.System.out.println("communicating
with bank via JSON REST transport");
10.}
11.
12.}
Code Listing: StandardAtmTransport making it @Default by removing any qualifiers from it
view source
print?
01.package org.cdi.advocacy;
02.
03.
04.//Just make sure
there are no qualifiers
05.public class StandardAtmTransport implements ATMTransport {
06.
07.public void communicateWithBank(byte[] datapacket) {
08.System.out.println("communicating
with bank via Standard transport");
09.}
10.
11.}
We also need to make sure that the beans.xml file is empty.
Code Listing: {classpath}/META-INF/beans.xml removing all alternatives
Code Listing: {classpath}/META-INF/beans.xml removing all alternatives
view source
print?
1.<beans xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.xsi:schemaLocation="
3.http://java.sun.com/xml/ns/javaee
4.http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
5.</beans>
Now use every transport that is installed using the
annotation.
view source
print?
01.package org.cdi.advocacy;
02.
03.import java.math.BigDecimal;
04.import java.util.Iterator;
05.
06.import javax.annotation.PostConstruct;
07.import javax.enterprise.inject.Any;
08.import javax.enterprise.inject.Instance;
09.import javax.inject.Inject;
10.import javax.inject.Named;
11.
12.@Named("atm")
13.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
14.
15.@Inject
16.private Instance<ATMTransport> allTransports;
17.
18.@PostConstruct
19.protected void init() {
20.System.out.println("Is
this ambiguous? " + allTransports.isAmbiguous() );
21.System.out.println("Is
this unsatisfied? " + allTransports.isUnsatisfied()
);
22.}
23.
24.public void deposit(BigDecimal bd) {
25.System.out.println("deposit
called");
26.
27.for (ATMTransport transport : this.allTransports) {
28.transport.communicateWithBank(null);
29.}
30.
31.}
32.
33.public void withdraw(BigDecimal bd) {
34.System.out.println("withdraw
called");
35.
36.for (ATMTransport transport : this.allTransports) {
37.transport.communicateWithBank(null);
38.}
39.
40.}
41.
42.}
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).
Output
view source
print?
1.Is this ambiguous? true
2.Is this unsatisfied? false
3.deposit called
4.communicating with bank via JSON REST
transport
5.communicating with bank via Soap
transport
6.communicating with bank via Standard
transport
7.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 andSoapRestAtmTransport transport class definition should have this //implements
ATMTransport {.
Now rerun the
example. You get this output.
Output
view source
print?
1.Is this ambiguous? false
2.Is this unsatisfied? false
3.deposit called
4.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:
view source
print?
1.Is this ambiguous? false
2.Is this unsatisfied? true
3.deposit called
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
Notice there a no
longer any ATMTransport transport implementations in the system at all.
The @Any qualifier states that you want all instances of an implementation. It
does not matter what qualifiers they have, you want them all @Jsons, @Soaps, @SuperFasts, whatever.
Add the all of the annotations we
commented out back to all of the transports. Add the @Any to the transport injection as follows:
Code Listing: AutomatedTellerMachineImpl @Inject @Any *`Instance`* to inject all transport instances
view source
print?
01....
02.import javax.enterprise.inject.Any;
03....
04.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
05.
06.@Inject @Any
07.private Instance<ATMTransport> allTransports;
08.
09.private ATMTransport transport;
10.
11....
12.}
The output of this should be: Output
view source
print?
1.Is this ambigous? true
2.Is this unsatisfied? false
3.deposit called
4.communicating with bank via JSON REST
transport
5.communicating with bank via Soap
transport
6.communicating with bank via Standard
transport
7.communicating with bank via the Super Fast
transport
@Any finds all of the transports in
the system. Once you inject the instances into the system, you can use the select method of instance to query for a particular type. Here is an example of that:
Code Listing: AutomatedTellerMachineImpl using selects to find a particular transport from the list you loaded
view source
print?
01....
02.import javax.enterprise.inject.Any;
03....
04.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
05.
06.@Inject @Any
07.private Instance<ATMTransport> allTransports;
08.
09.private ATMTransport transport;
10.
11.@PostConstruct
12.protected void init() {
13.transport =
allTransports.select(new AnnotationLiteral<Default>(){}).get();
14.
15.if (transport!=null) {
16.System.out.println("Found
standard transport");
17.return;
18.}
19.
20.transport =
allTransports.select(new AnnotationLiteral<Json>(){}).get();
21.
22.
23.if (transport!=null) {
24.System.out.println("Found
JSON standard transport");
25.return;
26.}
27.
28.
29.transport =
allTransports.select(new AnnotationLiteral<Soap>(){}).get();
30.
31.
32.if (transport!=null) {
33.System.out.println("Found
SOAP standard transport");
34.return;
35.}
36.
37.}
38.
39.
40.public void deposit(BigDecimal bd) {
41.System.out.println("deposit
called");
42.
43.transport.communicateWithBank(...);
44.}
45.
46....
47.}
Here is the expected format. Output
view source
print?
1.Found standard transport
2.deposit called
3.communicating with bank via Standard transport
Now imagine there
being a set of settings that are configured in a db or something and the code
might look like this to find a transport (this should look familiar to you by
now).
Code Listing: AutomatedTellerMachineImpl using selects and some business logic to decide which transport to use
view source
print?
01.package org.cdi.advocacy;
02.
03.import java.math.BigDecimal;
04.
05.import javax.annotation.PostConstruct;
06.import javax.enterprise.inject.Any;
07.import javax.enterprise.inject.Default;
08.import javax.enterprise.inject.Instance;
09.import javax.enterprise.util.AnnotationLiteral;
10.import javax.inject.Inject;
11.import javax.inject.Named;
12.
13.@Named("atm")
14.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
15.
16.@Inject @Any
17.private Instance<ATMTransport> allTransports;
18.private ATMTransport transport;
19.
20.private boolean useJSON = true;
21.private boolean behindFireWall = true;
22.
23.@SuppressWarnings("serial")
24.@PostConstruct
25.protected void init() {
26.
27.ATMTransport
soapTransport, jsonTransport, standardTransport;
28.standardTransport =
allTransports.select(new AnnotationLiteral<Soap>()
{}).get();
29.jsonTransport =
allTransports.select(new AnnotationLiteral<Json>()
{}).get();
30.soapTransport =
allTransports.select(new AnnotationLiteral<Default>()
{}).get();
31.
32.System.out.println(standardTransport.getClass().getName());
33.System.out.println(jsonTransport.getClass().getName());
34.System.out.println(soapTransport.getClass().getName());
35.
36.if (!behindFireWall) {
37.transport =
standardTransport;
38.} else {
39.if (useJSON) {
40.transport =
jsonTransport;
41.} else {
42.transport =
soapTransport;
43.}
44.}
45.}
46.
47.public void deposit(BigDecimal bd) {
48.System.out.println("withdraw
called");
49.transport.communicateWithBank(null);
50.
51.}
52.
53.public void withdraw(BigDecimal bd) {
54.System.out.println("withdraw
called");
55.
56.transport.communicateWithBank(null);
57.
58.}
59.
60.}
Exercise: Please combine the use of
Instance with a producer to define the same type of lookup but have the
business logic and select lookup happen in the TrasportFactory. 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.)
The dirty truth about CDI and Java SE
The dirty truth is this. CDI is part
of JEE 6. It could easily be used outside of a JEE 6 container as these
examples show. The problem is that there is no standard interface to use CDI
outside of a JEE 6 container so the three main implementations Caucho
Resin Candi, Red Hat JBoss Weld andApache
OpenWebBeans all have their own way to run a CDI
container standalone.
As part of the promotion and advocacy
of CDI, we (Andy Gibson, Rob Williams, and others) came up with a standard way
to run CDI standalone. It is a small wrapper around CDI standalone containers.
It works with Resin Candi, Weld and OpenWebBeans. If you used the
examples, in the CDI DI or this article then you used the
first artifact that the CDISource organization put together. We plan on coming
up with ways to unit test JPA outside of the container, and a few other things.
As we find holes in the CDI armor we want to work with the community at large
to fill the holes. CDI, although standard, is very new. We are hoping that the
groundwork that CDI has laid down can get used outside of Java EE as well as
inside of Java EE (we are not anti-Java EE).
Conclusion
Dependency
Injection (DI) refers to the process of supplying an external dependency to a
software component.
CDI is the Java standard for dependency
injection and interception (AOP). It is evident from the popularity of DI and
AOP that Java needs to address DI and AOP so that it can build other standards
on top of it. DI and AOP are the foundation of many Java frameworks. I hope you
share my vision of CDI as a basis for other JSRs, Java frameworks and
standards.
This article discussed more advanced
CDI dependency injection in a tutorial format. It covered some of the features
of CDI such as processing annotation data and working with multiple instances
of various types using the Instance class to tap into the powerful CDI class scanner capabilities.
CDI is a
foundational aspect of Java EE 6. It is or will be shortly supported by
Caucho's Resin, IBM's !WebSphere, Oracle's Glassfish, Red Hat's JBoss and many
more application servers. CDI is similar to core Spring and Guice frameworks.
However CDI is a general purpose framework that can be used outside of JEE 6.
CDI simplifies and sanitizes the API
for DI and AOP. Through its use of Instance and @Produces, CDI provides a pluggable architecture. With this pluggable
architecture you can write code that finds new dependencies dynamically.
CDI is a rethink on
how to do dependency injection and AOP (interception really). It simplifies it.
It reduces it. It gets rid of legacy, outdated ideas.
CDI is to Spring
and Guice what JPA is to Hibernate, and Toplink. CDI will co-exist with Spring
and Guice. There are plugins to make them interoperate nicely. There is more
integration option on the way.
This is just a
brief taste. There is more to come.
Resources
·
CDI
Depenency Injection Article
·
CDI
advocacy group
·
CDI
advocacy blog
·
CDI
advocacy google code project
·
Google
group for CDI advocacy
·
Manisfesto
version 1
·
Weld
reference documentation
·
CDI
JSR299
·
Resin
fast and light CDI and Java EE 6 Web Profile implementation
·
CDI
& JSF Part 1 Intro by Andy Gibson
·
CDI
& JSF Part 2 Intro by Andy Gibson
·
CDI
& JSF Part 3 Intro by Andy Gibson
No comments:
Post a Comment