In a normal Java application, it is the sole responsibility of the programmer to ensure that classes are instantiated. With CDI, however, the process is somewhat different, since its main component is a so-called “container” that creates instances of classes independently and removes them from memory as needed. The object diagram below shows the relationship between a container and the instances it manages. An object diagram is used in preference to a class diagram, since on this occasion, it is instances – not classes – that we wish to look at.
Fig. 3-1 Object diagram of a container and the instances it manages
With a small number of exceptions, the CDI container can manage instances of any concrete class and is used particularly to manage JavaBeans and EJB session beans. Classes managed by CDI are generally referenced as beans. A simple annotation is all that is required to ensure that CDI manages these classes as beans[1].
Incidentally, instances of beans do not possess a reference to the container; rather, the container possesses a reference to them. In the literature, this concept is also referred to as Inversion of Control (IoC).
Dealing with a container poses a number of questions, primarily questions regarding when the instances are created and deleted and how these instances are referenced. These and other questions will be answered in the following sections.
3.1 The Scope of a Bean
The point in time at which a bean is created and deleted depends on the scope of the bean. CDI also makes it possible to create our own scopes. However, the only ones of importance for us currently are the RequestScope, the SessionScope, the ViewScope and the DependentScope.
3.1.1 RequestScope
A bean with RequestScope is newly created for every incoming request. If the request is ended, the bean is deleted again (or, strictly speaking, is marked by the container for deletion; the actual deletion is then carried out at a later point by the Garbage Collector).
If multiple requests are received by the server simultaneously, a new bean is created for each. Data stored in the bean is lost when the request ends. Since a request usually has a very short lifespan (shorter than a second), beans with RequestScope are also very short-lived. For this reason, they’re not suited to saving data that’s required for a longer time, such as user data. However, these beans are more economical, since they do not bind to resources long-term.
It goes without saying that the container instantiates a bean with RequestScope only when the bean is referenced in the request; otherwise, resources would be used unnecessarily.
To specify that a bean has RequestScope, we must add the annotation @RequestScoped
(from the package javax.enterprise.context
) to the bean class.
3.1.2 SessionScope
A bean with SessionScope is created anew for each user session. The bean is only deleted when the user session is ended or when a timeout occurs due to a long period of inactivity.
This means that each user owns their own instance of this bean and thus that beans with SessionScope are ideally suited for storing user data. A good example in our application is the CampaignListProducer
, which contains the list of all of the user’s Campaign
objects.
Since a user session can last for a very long time (several hours), the server must sometimes cache an inactive user session to save on resources. In light of this, beans with SessionScope must be serializable – that is, they must implement the interface Serializable
from the java.io
package. The server can serialize such a bean, save it to the hard disk and de-serialize it again as needed. Beans with SessionScope therefore require more resources than beans with RequestScope and as such, beans should only obtain a SessionScope when absolutely necessary.
To specify that a bean has SessionScope, we must add the annotation @SessionScoped
(from the package javax.enterprise.context
) to the bean class.
3.1.3 ViewScope
One other scope for CDI beans, ViewScope[2], was introduced in Java EE 7 to facilitate better use of resources. This scope is exclusively of interest for applications that use JavaServer Faces[3]. Note, therefore, that although this scope is a CDI scope, it is provided by JSF since Verison 2.2, not CDI.
With this scope, the lifespan of the beans depends on the current view components. If a bean is referenced anew in a JSF view, a new instance of the bean is created. If the view components are deleted, the CDI container in turn removes the beans that were initialised within the view and that have the ViewScope. A JSF view usually exists across the span of multiple requests. For this reason, a bean with ViewScope has a longer lifespan than one with RequestScope.
This is of particular interest for beans that store data to be validated, since they must survive longer than a request. In the case of these beans, however, it would be a waste of resources to save them in the user session via SessionScope.
To specify that a bean has ViewScope, we must add the annotation @ViewScoped
(from the javax.faces.view
package) to the bean class. The differing package name makes it evident that the scope does not belong to CDI.
3.1.4 DependentScope
This scope simply means that the lifespan of the bean is dependent on another bean and therefore cannot exist independently of this other bean. A bean with DependentScope is created and deleted at the same time as the bean on which it is dependent.
The DependentScope is defined by the annotation @Dependent
from the package javax.enterprise.context
.
3.1.5 Setting the Scopes for Our Application
After all the theoretic background it is now time for the first code changes on the given sample application. Please click the link below and start the application as explained in Chapter 2:
If you check the sample applications code, you see that SessionScope is used for all our beans. This means that our application functions as desired, but that its use of resources is not as good as it could be.
To improve our use of resources, we can take advantage of the structure built up for the sample application:
Data that must survive longer than the lifespan of a JSF view have been stored in the CampaignListProducer
and CampaignProducer
classes. These data must continue to use SessionScope.
The controller beans – whose actual task is to manage the control flow – will be used, in a few cases, to save data that must be preserved beyond the lifespan of a view. Examples of such data are the Campaign
object for deletion in the ListCampaignsController
class and the donations that are created in DonateMoneyController
.
Since these data must be preserved over multiple requests, the RequestScope is not suitable to be used. The ViewScope, on the other hand, is ideal, since it deals exclusively with data that are required locally in the current view.
This is a conscious design decision: the controller beans are permitted solely to cache data that is local for the view.
Attentive readers will have noticed that not every controller bean would require a ViewScope. The ListDonationsController
, for example, could be managed solely using a RequestScope, since it does not store any data.
This said, we must remember that the decision to permit data to be saved is a design one. As such, this condition could potentially be changed for any bean at a later date. We will therefore now set the scope of all controller beans to the ViewScope, including the ListDonationsController
. While this admittedly results in less-than-optimal resource use, it does mean that all controller beans are now uniform in terms of their scope.
The authors take the view that this results in a clear design whilst also avoiding potentially confusing future discussions about why one controller bean is ViewScoped and another is RequestScoped.
For your convenience find below a link to our sample application with all the necessary scope changes done:
3.2 Referencing Beans Via Dependency Injection
So far, we have explained how a container instantiates and deletes a bean. What we’ve yet to discover is how a bean is referenced. This is done using the annotation @Inject
from the package javax.inject
. Consider the following snippet from the EditCampaignController
:
@Inject private CampaignListProducer campaignListProducer;
This indicates that the attribute campaignListProducer
is to store an instance of the bean CampaignListProducer
. Without the annotation @Inject
, the attribute would have an undefined value. For the container, however, this annotation serves as an indication that an instance of the type (in this case EditCampaignController
) should be referenced in the annotated attribute following the instantiation of the bean (in this case EditCampaignController
).
Thus, this annotation is used to make the reference of a bean available in another bean. This process is known as dependency injection. The relationships between the classes in our example are shown in the class diagram Fig. 3-2.
Fig. 3-2 Relationships between the container and the beans EditCampaignController and CampaignListProducer
At this point, we are still unsure as to which instance of CampaignListProducer
is taken at runtime. However, this is actually clearly defined, since the bean EditCampaignController
is bound to the current JSF view through the ViewScope. This, in turn, is assigned to a specific session – that of the current user. Furthermore, since the CampaignListProducer
bean is located in SessionScope, exactly one instance of this bean exists per user session. It is precisely this instance that is referenced in our example.
This said, what if the type of instance variable is a superclass or interface that is implemented by more than one bean? According to what we currently know, the container cannot unambiguously resolve the bean. This problem can be tackled using so-called qualifiers and is looked at in Chapter 4.
Incidentally, the annotation @Inject
can be used not only with attributes, but also with constructors or methods. This functionality is not required in our sample application. However, for the sake of completeness, we will show briefly how it would look below.
If the constructor is annotated with @Inject
, the other beans can be passed as parameters. In this case, the container passes the other beans at the time the bean is instantiated. In our example, this would look as follows:
private CampaignListProducer campaignListProducer; @Inject public EditCampaignController(CampaignListProducer campaignListProducer) { this.campaignListProducer = campaignListProducer; }
It is also possible to annotate methods with @Inject
. After the instantiation of the bean, the container invokes these methods and passes the beans that are specified in the method parameters. Here is the code for our example:
private CampaignListProducer campaignListProducer; @Inject public void setCampaignListProducer(CampaignListProducer campaignListProducer) { this.campaignListProducer = campaignListProducer; }
3.3 The Lifecycle
As we’ve already mentioned, beans are instantiated and deleted by the container. The programmer can specify methods to be invoked by the container at certain points within a bean’s lifecycle. In the following section, we will introduce the two lifecycle methods used by CDI. These are marked using the annotations @PostConstruct
and @PreDestroy
.
3.3.1 The PostConstruct Lifecycle Method
If the programmer annotates a method of the bean with the annotation @PostConstruct
from the package javax.annotation
, this method will be invoked immediately after the instantiation of the bean by the container.
Unlike the bean constructor, all dependencies to other beans are resolved at the time the method with the @PostConstruct
annotation is invoked. This means that the attributes annotated with @Inject
already contain the references to the beans and the methods annotated with @Inject
have already been invoked.
As an example, we will now take the beans DonateMoneyController
and CampaignListProducer
and replace the constructor with an initialization method init
that is annotated with @PostConstruct
.
For the DonateMoneyController
, this means that
public DonateMoneyController() { this.donation = new Donation(); }
is replaced by
@PostConstruct public void init() { this.donation = new Donation(); }
This has the tangible advantage that the method init
– unlike the constructor – can be invoked afresh, causing the bean to be reinitialized. This means that in the method doDonation
, the line
this.donation = new Donation();
can be replaced by
init();
Following the same logic, we will now replace the constructor for the CampaignListProducer
. In this case,
public CampaignListProducer() { campaigns = createMockCampaigns(); }
is replaced by
@PostConstruct public void init() { campaigns = createMockCampaigns(); }
At this stage, this does not offer us any extra advantage; in a later stage, however, we might use the method init
to reset the CampaignListProducer
, to provide the initial data after injecting the bean. By carrying out this step now, we are making preparations for future steps in the development of our application.
3.3.2 The PreDestroy Lifecycle Method
Methods of a bean annotated with @PreDestroy
– from the package javax.annotation
– are invoked by the container before the bean is deleted from storage. This enables the developer to free up resources that the bean has created – by deleting temporary files, for example.
Since this functionality is not required in our sample application, we will not make any changes of this nature here.
3.4 Use Any Class as a Bean with the Producer Method
Until now, the only classes we have been able to use as beans are those with either a parameterless constructor or a constructor annotated with @Inject
. Thanks to this convention, the container can produce instances of these beans independently. According to what we have learned so far, however, this is not possible for classes with other kinds of constructor.
CDI provides special methods to enable us to use a class of our choosing as a bean. These methods have the annotation @Produces
from the package javax.enterprise.inject
. Such producer methods create instances of user-selected classes, rendering them available as beans within the container and enabling them to be combined with the annotation @Inject
as discussed in section 3.2.
We will now use this functionality in the class Resources
, which contains a range of producer methods for making resources available to the application as beans. Listing 3-1 contains the source text of the class, which must be saved in the package press.turngeek.mycampaign.util
.
package press.turngeek.mycampaign.util; import javax.enterprise.context.Dependent; import javax.enterprise.context.RequestScoped; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.InjectionPoint; import javax.faces.context.FacesContext; import java.util.logging.Logger; @Dependent public class Resources { @Produces public Logger produceLog() { return Logger.getLogger("MyLogger", "messages"); } @Produces @RequestScoped public FacesContext produceFacesContext() { return FacesContext.getCurrentInstance(); } }
Listing 3-1 Resources class
The Resources
class provides a logger with the name MyLogger and the current FacesContext
. Both resources can be used in other beans by means of the annotation @Inject
.
A logger is a utility class that records outputs in a log file. The logger uses the resource bundle messages
for the internationalization of the log outputs. This means that messages issued by the logger must be defined in the files messages_en.properties
(for English) and messages_de.properties
(for German). We’ll talk more about this in just a moment.
The bean DonateMoneyController
makes direct use of the logger and the FacesContext
. Therefore, we will begin by injecting these into the bean as attributes:
@Inject private FacesContext facesContext; @Inject private Logger logger;
These resources can then be used in the method doDonation
. First, the line
final FacesContext facesContext = FacesContext.getCurrentInstance();
must be deleted, which will cause the injected instance variable facesContext
to be referenced in the method instead.
At this point, it also makes sense to issue a log message about the successful donation. This is where the logger comes into play. Through the changes we have made, the method doDonation
now appears as follows:
public String doDonation() { logger.log(Level.INFO, "log.donateMoney.thank_you", new Object[]{getDonation().getDonorName(), getDonation().getAmount()}); final ResourceBundle resourceBundle = facesContext.getApplication().getResourceBundle(facesContext, "msg"); final String msg = resourceBundle.getString("donateMoney.thank_you"); facesContext.addMessage( null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null)); init(); return Pages.DONATE_MONEY; }
The log message to be issued is selected by the key log.donateMoney.thank_you
belonging to the resource bundle messages
. We must now modify the resource bundle with the output text in our two target languages, English and German.
For English, insert the following line at the end of the file messages_en.properties
:
log.donateMoney.thank_you={0} has donated {1} Euro.
For German, add the equivalent line to the file messages_de.properties
:
log.donateMoney.thank_you={0} hat {1} Euro gespendet.
One drawback is the naming of the logger as MyLogger, which goes against the usual convention of naming the logger after the class that accesses it.
In order to assign the logger the name of its accessing class, we can share runtime information about the associated bean class with the producer method. To do this, the method produceLog
from the class Resources
is passed a parameter of the type InjectionPoint
from the package javax.enterprise.inject.spi
.
@Produces public Logger produceLog(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName(), "messages"); }
The expression injectionPoint.getMember().getDeclaringClass()
returns the Class
object of the accessing class, which integrates the logger using the annotation @Inject
. If the logger is now used in the bean DonateMoneyController
, it will have the name DonateMoneyController – or in general terms, the name of the accessing bean.
Since instances created by the producer methods are CDI beans, these also have a scope. By default, this will be scope of producer method’s class, so in our case it is the DependentScope (see section 3.1.4). This can be changed if so desired by annotating the producer method with the desired scope.
In our case, the logger stays on DependentScope in order to ensure that a fresh instance of the logger is created for each accessing bean. However, the producer method for the FacesContext
will be set to RequestScope, since this object does not change within a request.
Furthermore, Java EE internally provides a producer method for the HttpServletRequest
. We will use this now by replacing the method getAppUrl
in the EditDonationFormController
with the following snippet of code:
@Inject private HttpServletRequest req; private String getAppUrl() { String scheme = req.getScheme(); String serverName = req.getServerName(); int serverPort = req.getServerPort(); String contextPath = req.getContextPath(); return scheme+"://"+serverName+":"+serverPort+contextPath; }
As a result of this change, the current request’s HttpServletRequest
will be injected in the controller and used directly in the method getAppUrl
. Previously, it was necessary to retrieve this via the FacesContext
every time the method was accessed.
3.5 Producing domain objects
So far we have used producer methods for creating technically related objects like Logger
and HttpServletRequest
. In this section we will see they are also very helpful for providing domain related data objects. There are two domain related components in our application that could benefit from producer methods, namely the beans CampaignProducer
and CampaignListProducer
.
The task of the CampaignListProducer
is to provide a list of Campaign
objects. A class wishing to use this list does not actually require a dependency to the CampaignListProducer
, but rather solely to this list. To make this possible, we must annotate the getCampaigns
method of the CampaignListProducer
with @Produces
. Since no other bean in the application provides a list of Campaign
objects, this list can now be injected in other beans as shown below:
@Inject private List<Campaign> campaigns;
This being said, we actually want to refrain from making any changes of this nature, since the following section will introduce another method for avoiding dependencies to the CampaignListProducer
: application-wide messages.
Nonetheless, we still want to use the new producer method getCampaigns
so that we can reference it in Facelets. To do this, we will additionally add the @Named
annotation to this method and remove the same annotation at the class level.
This enables the JSF DataTable component of the file listCampaigns.xhtml
to access the list of Campaign
objects directly using the name campaigns
and removes the need for it to detour via the CampaignListProducer
. To implement this change, modify the following lines of the file listCampaigns.xhtml
:
<p:dataTable value="#{campaignListProducer.campaigns}" var="campaign">
to the following expression:
<p:dataTable value="#{campaigns}" var="campaign">
As a result of this, the class CampaignListProducer
will now contain a producer method that provides a list of Campaign
objects and thus does what it says on the tin! You see now that the naming with the suffix Producer
wasn’t a coincidence.
Following the same principle, we now want to introduce similar producer methods for the bean CampaignProducer
. To do this, we will remove the @Named
bean annotation at the class level and add the annotations @Named
and @Produces
to the getter methods getSelectedCampaign
and isAddMode
.
For the latter, this results in the following definitions:
@Produces @Named public Campaign getSelectedCampaign() { return campaign; }
and
@Produces @Named public boolean isAddMode() { return mode == Mode.ADD; }
Following this change, we can use the producer methods directly in the Facelets. To do this, we must replace all references to the getter in the files editCampaign.xhtml
and listDonations.xhtml
. As we do this, we need only to take the EL expressions – that is, everything inside #{
and }
– into account.
In specific terms, this means that we must access the Facelets and replace the expressions campaignProducer.selectedCampaign
and campaignProducer.addMode
with selectedCampaign
and addMode
respectively. Since we are dealing with a few different references, our best option is to use the Search and Replace function in our editor. Lastly, it’s important to be careful while we work and to test any changes in order to avoid problems at a later stage. Worst case we have you covered with the following Cloud IDE link:
Discussion
Use the message board below to give the authors your feedback or to discuss this page’s topic with other readers (in English please!). Please don’t expect the authors to answer directly, but they might update the content of this site according to your feedback.
- Before CDI 1.1, it was necessary for an additional empty file called WEB-INFbeans.xml to be contained in the application archive in order for activation to occur. ↵
- The ViewScope actually existed in JSF before version 2.2; however, it could only be used for JSF managed beans, not for CDI beans. ↵
- SessionScope and RequestScope, in contrast, do not require JSF – they function with any application that is built solely on servlets. ↵