Everything you knew about Java beans was redefined by the CDI (Contexts and Dependency Injection) concept. At the abstract level, a CDI bean is not like a common Java bean; it is more like a "source of contextual objects that define application state and/or logic". However, at the same time, a CDI bean is like any Java bean, because it unifies the Java beans under the same roof. At the container level, a CDI bean is a Java EE component that has a container controllable lifecycle, according to the lifecycle context model defined in the CDI specification.
At the development level, a CDI bean contains an entire set of provisions:
- A CDI scope (it can be any of the following: request (
@RequestScoped
), session (@SessionScoped
), application (@ApplicationScoped
), dependent (@Dependent
), conversation (@ConversationScoped
) - A set of bean types -- empty not allowed
- A set of qualifiers (used for various implementations of a particular bean type -- defined as
@Target({METHOD, FIELD, PARAMETER, TYPE})
and@Retention(RUNTIME))
-- empty not allowed - A set of interceptor bindings
- A bean implementation
- A bean EL name (make a bean accessible through the EL using the
@Named
built-in qualifier) -- optional
Starting with Java EE 6, CDI becomes an integral part of and a powerful facility for Java EE components. Employing CDI, you can develop Java EE components that exist within the lifecycle of an application with well-defined scopes. In addition, CDI allows Java EE components such as JavaServer Faces (JSF) managed beans to be injected and makes it easy for developers to use enterprise beans along with JSF technology in Web applications. CDI is specified by JSR 299, also known as Web Beans, and the related specifications that CDI uses include Dependency Injection for Java (JSR 330) and Managed Beans (JSR 316).
However, before that, let us have a short overview of the CDI managed bean concept.
Overview of CDI Managed Beans
A top-level Java class is a managed bean if it is defined to be a managed bean by any other Java EE technology specification (like JSF), or if it meets the below set of conditions:
- It is not a nonstatic inner class.
- It is a concrete class or is annotated
@Decorator
. - It is not annotated with an EJB component-defining annotation or declared as an EJB bean class in ejb-jar.xml.
- It has an appropriate constructor. That is, one of the following is the case:
- The class has a constructor with no parameters.
- The class declares a constructor annotated
@Inject
.
Note: No special declaration, such as an annotation, is required to define a managed bean.
Lifecycle-specific Actions for a CDI Managed Bean
CDI managed beans support the PostConstruct
and PreDestroy
lifecycle callbacks.
- The
PostConstruct lifecycle
callback is represented by the@PostConstruct
annotation. This annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. The@PostConstruct
annotated method will be called by the container after the bean has been constructed and before any business methods of the bean are executed. - The
PreDestroy lifecycle
callback is represented by the@PreDestroy
annotation. This annotation is used on methods to signal that the instance is in the process of being removed by the container. A@PreDestroy
annotated method will execute directly after the@Remove
method has finished executing and before the bean instance is removed by the container. After this method has executed the bean can be garbage collected.
Before applying these annotations, you need a CDI managed bean. You can develop a simple CDI managed bean, like below -- your bean is in RequestScope
and has a String property, named ballType, and an action method, named generateRandomBall
. The generateRandomBall
generates an int and stores a random ball type (a random text) in the ballType
property:
package com.games.balls;
import java.util.Random;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class BallBean {
private String ballType;
private Random generator = new Random();
public void generateRandomBall() {
int randomValue = generator.nextInt(3);
switch (randomValue) {
case 1:
this.ballType = "Tennis Ball";
System.out.println("*** Tennis Ball Generated ***");
break;
case 2:
this.ballType = "Volley Ball";
System.out.println("*** Volley Ball Generated ***");
break;
case 3:
this.ballType = "Football Ball";
System.out.println("*** Football Ball Generated ***");
break;
default:
this.ballType = "No Ball";
System.out.println("*** No Ball Generated ***");
}
}
public String getBallType() {
return ballType;
}
public void setBallType(String ballType) {
this.ballType = ballType;
}
}
The method generateRandomBall
is called from the Facelets code listed below:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml"
xmlns:h="https://java.sun.com/jsf/html">
<h:head>
<title>Get a random ball</title>
</h:head>
<h:body>
<h:form id="myBall">
<p><h:outputLabel value="Press the button to get a random ball ..."/></p>
<p><h:commandButton value="Get ball" action="#{ballBean.generateRandomBall}"/></p>
<p><h:outputText value="#{ballBean.ballType}"/></p>
</h:form>
</h:body>
</html>
If you test the application at this moment, then in the GlassFish logs you will find an output like below:
[#|2011-01-10T06:06:42.693-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** No Ball Generated ***|#]
[#|2011-01-10T06:06:44.704-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=27;_ThreadName=Thread-1;|*** No Ball Generated ***|#]
[#|2011-01-10T06:06:45.757-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** Tennis Ball Generated ***|#]
[#|2011-01-10T06:06:46.535-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=27;_ThreadName=Thread-1;|*** Volley Ball Generated ***|#]
[#|2011-01-10T06:06:47.248-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** Tennis Ball Generated ***|#]
If you think of the random generator as a resource, then it will be elegant to have a @PostConstruct
annotated method for creating it and a @PreDestroy
annotated method for destroying it. Since the @PostConstruct
annotated method is called before any other business logic method is called (in your case the generateRandomBall
method) the generator will be available just on time. In addition, before the bean is destroyed the generator can be also destroyed into a @PreDestroy
annotated method. You can modify the above bean, like this:
package com.games.balls;
import java.util.Random;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class BallBean {
private String ballType;
private Random generator;
@PostConstruct
private void startGenerator(){
System.out.println("*** RANDOM GENERATOR CREATED ***");
generator = new Random();
}
@PreDestroy
private void stopGenerator(){
System.out.println("*** RANDOM GENERATOR DESTROYED ***");
generator = null;
}
public void generateRandomBall() {
int randomValue = generator.nextInt(3);
switch (randomValue) {
case 1:
this.ballType = "Tennis Ball";
System.out.println("*** Tennis Ball Generated ***");
break;
case 2:
this.ballType = "Volley Ball";
System.out.println("*** Volley Ball Generated ***");
break;
case 3:
this.ballType = "Football Ball";
System.out.println("*** Football Ball Generated ***");
break;
default:
this.ballType = "No Ball";
System.out.println("*** No Ball Generated ***");
}
}
public String getBallType() {
return ballType;
}
public void setBallType(String ballType) {
this.ballType = ballType;
}
}
If you test the application at this moment, then in the GlassFish logs you will find an output like below -- notice the flag messages that marks the @PostConstruct
and @PreDestroy
effects. Since the bean is in RequestScope, the generator is created and destroyed after each HTTP request is accomplished:
[#|2011-01-10T06:18:09.487-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR CREATED ***|#]
[#|2011-01-10T06:18:09.491-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR DESTROYED ***|#]
[#|2011-01-10T06:18:11.664-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR CREATED ***|#]
[#|2011-01-10T06:18:11.664-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** No Ball Generated ***|#]
[#|2011-01-10T06:18:11.668-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR DESTROYED ***|#]
[#|2011-01-10T06:18:12.400-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR CREATED ***|#]
[#|2011-01-10T06:18:12.400-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** Tennis Ball Generated ***|#]
[#|2011-01-10T06:18:12.408-0800|INFO|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=26;_ThreadName=Thread-1;|*** RANDOM GENERATOR DESTROYED ***|#]
If you modify the bean scope as SessionScope
-- do not forget to implement the Serializable
interface for passivation tasks -- then the generator will be created when the session starts, and will be destroyed when the session is destroyed.