Introducing CDI
CDI was originally developed via the Java Community Process (JCP) as JSR-299. Origi- nally JSR-299 was called WebBeans, but it quickly became evident that the features encompassed within JSR-299 went far beyond replacing the oft-maligned JSF-managed beans.
A managed bean is simply a POJO that’s managed by a container, with the container providing a basic set of services including resource injec- tion, lifecycle callbacks, and interceptors. A managed bean must have a no-argument constructor, not be serializable, and possess a unique name. Managed beans are defined using the @ManagedBean annotation with annotations for lifecycle callbacks: @PostConstruct and @Destroy.
CDI Services
- EJB 3 dependency injection is limited to only EJBs; you can’t perform injection into any POJO or inject a non-EJB into an EJB.
- CDI’s dependency injection doesn’t have these limitations
- CDI is an object container that can be used as a standalone or within an existing Java EE container.
- The CDI container provides a lifecycle for stateful objects, binding of objects to well-defined contexts, type-safe dependency injection, an event notification facility, and robust interceptors.
Lifecycle for stateful objects
CDI provides a well-defined lifecycle for its beans. CDI is an extension of the managed beans specification and thus provides a number of hooks for controlling the creation of new objects, as well as notification of object destruction.
Contexts
Most web applications have at least two contexts or scopes: application and session.
- The data stored in the
application scopeis shared—it isn’t tied to one particular user. Session scope, on the other hand, is asso- ciated with a specific user—typically a browser window.
In the case of a travel website, a user might have two tabs open and be comparing flights to Rome on two different days or comparing two different hotels. The user thus has two different contexts that must be tracked separately.
In addition, the user may have created a nested context by launching a wizard in one window to not only pick a hotel but also book a car at the same time. As you can see, application scope and session scope are suddenly not enough; you need additional contexts with which to build your applications.
This is where CDI comes into play. It introduces additional contexts and provides a simple mechanism by which you can define additional contexts.
CDI comes with four built-in contexts or scopes:
- Application
- Conversation
- Request
- Session
Besides these four scopes, there are also two pseudo-scopes:
singleton: handles the special case of singleton beans.dependent:- The
dependent scopeis the scope that a bean is assigned to by default if the bean isn’t assigned explicitly to a scope. - An object that belongs to the
dependent scopeis created when the object it belongs to is created and is destroyed when its owning object is destroyed.
- The
CDI has a pluggable architecture and with a little code, you can develop your own contexts.
Type-safe dependency injection
- Unlike other DI frameworks, CDI doesn’t use string-based identifiers to determine what object should be injected. Instead, CDI uses type information provided by the Java object model to determine what object should be injected.
- In situations where the determination is ambiguous because multiple Java objects match the type, quali- fier annotations are used to select the correct candidate.
Event notification
ActionListenerEvent and having the listener registration performed automatically.
Interceptors
Interceptor support in both EJB and CDI is specified in JSR-381 Interceptors 1.1.
- With CDI, interceptors aren’t limited to EJBs and can be used on any bean managed by CDI.
- With CDI, interceptors can be used with business methods on a bean, as well as lifecycle and timeout callbacks.
- Related to interceptors, CDI also introduces a new construct called
decorators. Adecoratoris an interceptor that’s tied to a specific interface.
Relationship between CDI and EJB 3
- EJB beans are still managed by the EJB container. Thus, the EJB container handles transactions and con- currency and provides all of the supporting functionality.
- CDI manages its own beans but also provides services to EJBs.
- You can think of CDI beans as object containers that provide injection, events, interceptors, and scoping support to objects that don’t need the full set of services provided by the EJB container.
- EJB—where you need transaction support, security, and so on.
- CDI is the container for your POJOs
- CDI and EJB are fully integrated. This means that EJBs can use all of the services we just discussed including dependency injection, event notification, interceptors, and decorators.
- To use the CDI variant because they’re more powerful, generic, and not limited to just EJBs. As you’ll see, you can safely use the
@Injectinstead of the@EJB. - CDI’s DI will support the injection of EJBs into CDI beans. This eliminates the need to write code that will retrieve beans from JNDI. Just add the @Inject annotation and CDI will take care of the rest. Thus, using EJBs has never been easier—you can treat an EJB bean just like any other POJO.
- Furthermore, if a CDI bean takes on additional functionality, you can simply add a
@Stateless,@Singleton, and@Statefulannotation and make your bean an EJB.
CDI isn’t a replacement technology for EJBs but a powerful enhancement. CDI not only empowers EJB, it also greatly augments JSF .
Relationship between CDI and JSF 2
CDI beans
Unlike EJB, CDI doesn’t have its own component model. A bean within CDI can be a managed bean (JSF), Enterprise Java Bean, or POJO. All of these types of objects can use the full range of services provided by CDI.
A CDI bean is associated with a context, has a type, and may be qualified. The context determines the lifecycle of the bean, whether it lives for only one request or it’s a part of a conversation or workflow.
How to use CDI beans
- The creators of CDI took the approach of convention over configuration.
If your application container supports CDI, which it does if it’s at least compliant with Java EE 6, then all you have to do is place a beans.xml file in your application. The beans.xml file serves two purposes: it’s a configuration file for CDI and it’s a marker file so that CDI knows whether it needs to scan the JAR file for beans.
<?xml version="1.0" encoding="UTF-8"?>
<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>
Component naming and EL resolution
@Named: CDI provides an annotation,@Named, which must be placed on classes or pro- ducer methods
package javax.inject;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
- Referencing employee instance from
editEmployee.xhtml
Bean scoping
Scopes/contexts in CDI
| Scope | Description |
|---|---|
@ApplicationScoped | An instance is created only once for the duration of the application and is destroyed once the application is terminated. |
@Dependent | An instance is created each time an injection is performed. This is the default scope of a bean and is used the vast majority of the time. |
@ConversationScoped | This is a new scope type that was introduced with CDI but existed in JBoss’s Seam. It’s a scope that’s programmatically controlled by the application. It spans multiple requests but is shorter than the session scope. In a web application, conversations are used for a task that spans multiple page requests. For example, this is how you’d handle the situations where a user is booking two different vacations in two different browser tabs. |
@RequestScoped | This scope corresponds to the standard HTTP request. It begins when a request arrives and is discarded when the response is rendered. |
@SessionScoped | This scope corresponds to the HTTP session. References live for the duration of the session. |
If no annotation is placed on a bean, the bean is automatically assigned to the dependent scope.
Conversation scope
- A bean is marked as belonging to a conversation by adding the
@ConversationScopedannotation to the bean definition. - By default, a conversation is associated with the current request scope and is terminated when the current request is destroyed.
Next generation of DI
Injection with @Inject
- Using constructor injection.
In the first example you could have easily used the @EJB annotations. The real power of the @Inject annotation is apparent when you annotate the constructor. This is a more natural approach—you don’t need to use the @PostConstruct annotation to do setup operations after the bean is created and injection is performed. Using injection with constructors enables the beans to behave like regular POJOs—parameters are passed in via the constructor and the object initializes itself like any other object. You don’t have to do injection and then use a callback to initialize the bean.
Producer methods
producer method: A producer is a method or instance variable that’s consulted by CDI to create a bean instance. Marked with the@Producerannotation.
- Invoking the producer from
index.xhtml
CDI will scan all of the classes in a JAR file—the JAR file containing a beans.xml file. It’ll keep track of all producer methods it discovers while analyzing the classes in the JAR file. When an instance of the bean is requested—a user as in the case of this example—CDI will invoke the producer method if an instance of the bean doesn’t already exist in the current scope.
Producer methods and JPA
- Most of the time, the fact that the injected bean is a proxy object isn’t an issue.
- But JPA implementations often do care.
- If you try to pass a bean instance created by CDI to JPA, JPA will claim that the bean can’t be persisted and that it isn’t a known type.
- To solve this problem, you’ll need to use a
producermethod and keep a reference to the original POJO.
Anyone who gets a reference via injection will be working with a proxy.
Using qualifiers
qualifier: Aqualifierenables you to qualify certain injection points as using different instances of a bean.
The following code snippet defines the @AuthenticatedUser and @Seller qualifiers:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface AuthenticatedUser {}
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Seller {}
After defining the qualifier, you need to add the qualifier to the producer method:
@Produces @SessionScoped @AuthenticatedUser @Named("currentUser")
public User getCurrentUser() {
...
}
Once the qualifier has been defined, you can use it to tell CDI which instance you want injected, as shown in the next listing.
Disposer methods
- A
disposermethod is responsible for handling the destruction of a bean.
Implementing a disposer method involves adding the @Disposes annotation to a method parameter. Qualifiers may also be used. Additional parameters can be specified; CDI will attempt to resolve the additional parameters as beans using qualifiers if provided. When the disposer method is invoked, the code can close database connections and so on.
Specifying alternatives
With different deployment scenarios, you need a mechanism at runtime to configure an alternative for an injection point. Obviously this mechanism must use a configuration file and not require code changes.
To mark a bean as being an alternative, add the @Alternative annotation to the class. To use the alternative, enable it in the beans.xml configuration file. Depending on the target environment for the build, you’d optionally use a different beans.xml file or dynamically generate one.
To enable DevelopmentStartup, you’d add the following configuration entry to the beans.xml file:
<alternatives>
<class>com.actionbazaar.setup.DevelopmentStartup</class>
</alternatives>
Interceptor and decorators
- A
decoratoris used to implement specific business functionality for an interface that’s abstracting into a crosscutting concern. - A traditional
interceptor, on the other hand, provides generic crosscutting logic.
Interceptor bindings
Creating an interceptor with CDI involves four different coding tasks:
- Create an interceptor annotation.
- Create an interceptor implementation annotated with the interceptor annotation.
- Annotate targeted instances with the interceptor annotation.
- Enable the interceptor with the
beans.xmlfile.
Interceptor types
| Interceptor type | Annotation | Description |
|---|---|---|
| Business method | @AroundInvoke | Intercepts business method invocations |
| Lifecycle callback | @PostConstruct/@PreDestroy | Intercepts lifecycle callbacks |
| EJB timeout | @AroundTimeout | Intercepts EJB timeout methods |
- Example
@InterceptorBinding
@Retention(RUNTIME)
@Target({TYPE})
public @interface PerformanceMonitor {}
Methods on InvocationContext
| Method | Description |
|---|---|
Object getTarget(); | Returns the object instance you’re performing interception on |
Object getTimer(); | Returns the timer object associated with the timeout |
Method getMethod(); | Returns the method on which you’re performing interception |
Object[] getParameters(); | Returns the parameters that are to be passed to the method |
void setParameters(Object[] os); | A setter enabling you to change the parameters used to invoke the target method |
Map<String, Object> getContextData(); | Returns context data associated with this invocation—information from the annotation |
Object proceed() throws Exception; | Proceeds with invoking the method |
- To use it on the
LandingController, you’d add it as follows:
@Named
@ApplicationScoped
@PerformanceMonitor
public class LandingController {
...
}
Adding the annotation to the LandingController doesn’t enable it by default. Interceptors must be enabled in the beans.xml file. The following snippet enables this interceptor:
<interceptors>
<class>com.actionbazaar.util.PerformanceInterceptor</class>
</interceptors>
Decorators
But unlike an interceptor, you’re overriding the methods you want to intercept. A decorator extends or implements the interface of the bean it’s intercepting. As a result, a decorator is tightly coupled to the bean and can implement business logic.
Once again, decorators are disabled by default. To enable the preceding decorator, you’ll need to add the following snippet to the beans.xml file:
<decorators>
<class>com.actionbazaar.buslogic.BidManagerFraudDetector</class>
</decorators>
Component stereotypes
@Named @SessionScoped
public class CurrentUserBean implements Serializable {
@Produces @SessionScoped @AuthenticatedUser @Named
public User getCurrentUser() {
...
}
}
@SessionScoped
@AuthenticatedUser
@Named
@Stereotype
@Target( { TYPE, METHOD, FIELD })
@Retention(RUNTIME)
public @interface CurrentUser {}
With this new annotation, you’ve consolidated @SessionScoped, @AuthenticatedUser , and @Named down to @CurrentUser . You can see this annotation in action in the simplified code for the CurrentUserBean:
@Named @SessionScoped
public class CurrentUserBean implements Serializable {
@Produces @CurrentUser
public User getCurrentUser() {
...
}
}
There are some ground rules for defining stereotypes. Stereotypes can encapsulate the following CDI annotations:
- Scope
- Interceptor bindings
- Named annotation for JSF integration
- Alternative annotation
The @Model annotation is defined as follows
@Named
@RequestScoped
@Documented
@Stereotype
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Model {}
@Model
public class ItemController implements Serializable {
...
}
Injecting events
The @Observes annotation is placed on one or more parameters of the method that’s to receive an event.
As you can see from this listener, CDI handled event registration for you. You didn’t need to implement an interface or acquire a reference to the ItemController to register this class as a listener. The onNewItemmethod is a listener because the item parameter is annotated with @Observes. The instances of item that you’ll receive are restricted to only new items with the @NewItem qualifier.
Using conversations
Conversations are central to how most users interact with their web browsers.
A conversation-scoped bean will exist for the duration of a conversation, which is less than that of a session but greater than that of a request. Thus, a conversation spans multiple page requests and represents a subunit of work.
Methods on conversation
| Method | Description |
|---|---|
void begin() | Converts the current transient conversation into a long-running conversation |
void begin(String id) | Converts the current transient conversation into a long-running conversation with a custom ID |
void end() | Terminates the conversation and converts it to a transient conversation |
String getId() | Returns the ID of the current conversation |
long getTimeout() | Returns the timeout of the current conversation in milliseconds |
boolean isTransient() | Returns true if the current conversation is transient and will be lost at the end of the request |
void setTimeout(long timeout) | Sets a custom timeout for the current conversation; only applicable if the conversation isn’t transient |
Example
- Item page -view and bid
placeBid.xhtml-confirms the bid
BidController-concludes placing a bid
Using CDI effectively with EJB 3
- CDI was added in Java EE 6 and its integration has contin- ued to grow with Java EE 7.
- EJB and CDI are complementary technologies that enable one another and enable robust Java EE apps to be built relatively easily.
- CDI greatly simplifies the amount of work necessary to use EJBs—there’s no need to access JNDI or write a custom JNDI abstraction layer.
- CDI introduces dependency injection and context features as well as some extensions to POJOs.
- CDI isn’t a replacement for EJBs—EJBs are necessary when security, transactions, remoting, scheduling, asynchro- nous support, and locking are required.
- EJBs are beans within CDI, so you can approach the problem as if everything is a CDI bean, and you upgrade a CDI bean to an EJB when you require the Enterprise services provided by the EJB container
Because EJBs are also CDI beans, one very important practice is to use @Inject instead of @EJB. The configurability provided by CDI makes testing an application much easier.
@Inject makes it irrelevant whether a bean is an EJB or simply a POJO—at any point in the future, you can upgrade the bean from a POJO to an EJB as the application evolves.