CHAPTER 12

image

Contexts and Dependency Injection

One of the most important features in Java EE is Contexts and Dependency Injection (CDI). CDI helps bind the web tier and the business logic or transactional tier of the Java EE platform together. CDI makes it easy to expose business objects for use within JSF web views so that developers can directly bind JSF view widgets to public JavaBean members and methods. It also provides facilities that make it possible to inject JavaBean classes and resources into other Java objects in a type-safe and efficient manner.

CDI is architected from two methodologies: contexts and dependency injection. Contexts provide the ability to bind the life cycle and interactions of stateful components to well-defined but extensive contexts, per Oracle’s Java EE 7 Tutorial. In the same tutorial, dependency injection is defined as the ability to inject components into an application in a type-safe way, including the ability to choose at deployment time which implementation of a particular interface to inject. To make use of CDI, a developer should become familiar with a series of annotations that can be used to decorate objects and injected components. This chapter covers recipes that will demonstrate such annotations and where they should be used.

Since CDI provides a high level of loose coupling, it is an important piece of any Java enterprise application. Those applications that make use of CDI in the right way can become very efficient because CDI provides a decoupling of resources, as well as strong typing, by eliminating the requirement to use String-based names for managed resources and by using declarative Java annotations to specify just about everything. Although it is possible to develop Java EE applications without the use of CDI, it is very easy to use and enables enterprise applications to become more robust and efficient than those that do not use CDI features.

12-1. Injecting a Bean or Other Object

Problem

You want to use a bean or other object from within another class.

Solution

Utilize dependency injection to make the bean or object available from within another class. The following class, located in the JavaEERecipes project, represents an object that can be injected into another class:

package org.javaeerecipes.chapter12;

public class CalculationBean {
    
    public int addNumbers(int[] numArray){
        int temp = 0;
        for(int x : numArray){
            temp = temp + x;
        }
        return temp;
    }
    
}

As you can see, the CalculationBean class represents a standard Java object. This object can be injected into another class by using the @Inject annotation. The following class, located in the same package as CalculationBean within the sources, demonstrates how to inject an object. Note that CalculationBean is never specifically instantiated; rather, it is injected or obtained via the declaration of an annotation.

package org.javaeerecipes.chapter12;

import javax.inject.Inject;

public class UsingClass {
    
    @Inject
    CalculationBean calcBean;
    
    public void performCalculation(){
        int[] intarr = new int[2];
        intarr[0] = 2;
        intarr[1] = 3;
        System.out.println("The sum of 2 + 3:" + calcBean.addNumbers(intarr));
    }
    
}

In the example, @Default CalculationBean is injected into the bean. Once the bean or resource is injected into another Java class, it can be referenced as if it were local to the class into which it was injected.

How It Works

The concept of dependency injection greatly reduces the amount of overhead that is necessary for a developer in order to gain reference to a Java object from within another Java class. The Java EE stack makes it very easy to gain reference to just about any Java object from within another class. Dependency injection refers to the ability to inject components into an application in a type-safe manner, including the ability to choose at deployment time which implementation of a particular interface to inject. CDI allows almost any Java class to be injected into another with very little configuration. This ability increases the usability of resources since such resources can be referenced from any number of different classes and maintain the same state wherever they are being used. In reality, just about any object can be injected anywhere with CDI. The following are some Java objects that can be injected:

  • Almost any Java class
  • Session beans
  • Java EE resources: data sources, JMS topics, queues, connection factories
  • Persistence contexts
  • Producer fields
  • Objects returned by producer methods
  • Web service references
  • Remote EJB references

To inject a resource into another, the application module or JAR file must contain a META-INF directory that includes a beans.xml configuration file. The beans.xml file may or may not be empty depending upon the configuration. However, for the purposes of this example (and for most general CDI use cases), the beans.xml file is simply an empty configuration file that is used as a placeholder to tell Java EE that the application wants to use CDI. Next, the javax.inject.Inject annotation (@Inject) must be used to denote the class being injected by annotating a class variable of the object type. For instance, if you want to inject a Java object of TypeA, you would declare a class variable of type TypeA and annotate it with @Inject, as follows:

@Inject
TypeA myTypeVar;

Once said injection is performed, the declared variable can be utilized throughout the class because it is a direct reference to the original class of the specified Java type. By defining a specific scope to the injection bean (Recipe12-5), you can indicate whether an injected object will cause the instantiation of a new object of that type or whether it will look up an existing object of that type and reuse it. By far, one of the most convenient and useful cases for using CDI is the ability to inject a managed bean into another object and make use of its current state, as if its contents existed everywhere.

CDI provides type-safe injection because there is no need to specify a String-based name in order to instantiate or refer to another object. By maintaining declared variables that are used as points of injection, the variable name itself provides for strong typing and thus reduces the number of errors that may arise.

12-2. Binding a Bean to JSF Views

Problem

You want to bind a JavaBean to a JSF view using EL.

Solution #1

Denote a class with the @Named annotation, and specify a name for the class in String format. The String that is specified within the @Named annotation can be used to gain reference to the bean from within a JSF view. The following example demonstrates the binding of a bean field and method to a JSF view. The following Java class, named CalculationBean, is a CDI managed bean that contains the @Named annotation, specifying myBean as the bean reference name:

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named("myBean")
@RequestScoped
public class CalculationBean implements java.io.Serializable{
    
    private int num1 = 1;
    private int num2 = 0;
    private int sum;
    
    public CalculationBean(){
    }
    
    public void addNumbers(){
        System.out.println("Called");
        setSum(getNum1() + getNum2());
    }

    /**
     * @return the num1
     */
    public int getNum1() {
        return num1;
    }

    /**
     * @param num1 the num1 to set
     */
    public void setNum1(int num1) {
        System.out.println("setting num1");
        this.num1 = num1;
    }

    /**
     * @return the num2
     */
    public int getNum2() {
        return num2;
    }

    /**
     * @param num2 the num2 to set
     */
    public void setNum2(int num2) {
        this.num2 = num2;
    }

    /**
     * @return the sum
     */
    public int getSum() {
        return sum;
    }

    /**
     * @param sum the sum to set
     */
    public void setSum(int sum) {
        this.sum = sum;
    }
    
}

The bean is bound to the JSF view via the String-based name myBean, making a seamless binding between the web view and the back-end business logic. The following JSF view contains three fields and a commandButton action that are bound to myBean via the JSF EL:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Recipe 12-2: Binding a Bean to JSF Views</title>
      
    </h:head>
    <h:body>
        <p>
            <h:form>
            <h:inputText value="#{myBean.num1}"/>
            <br/>
            <h:inputText value="#{myBean.num2}"/>
            
            <br/><br/>
            Sum: <h:outputText id="sum" value="#{myBean.sum}"/>
            
            <br/><br/>
            <h:commandButton value="Calculate" type="submit" action="#{myBean.addNumbers()}">
                
            </h:commandButton>
            </h:form>
        </p>
    </h:body>
</html>

Solution #2

Denote the class using the javax.inject.Named (@Named) annotation, without using any String-based designation for the class. When the @Named annotation is specified without providing a String-based name designation, a binding name will be derived from the class name, converting the first letter of the class name to lowercase. For the following example, assume that the class CalculationBean that was referenced in Solution #1 is going to be referenced from within a JSF view via EL, except there will be no String-based identifier specified within the @Named annotation. Since the @Named annotation does not specify a name, the EL would refer to the class name as such:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Recipe 12-2: Binding a Bean to JSF Views</title>
      
    </h:head>
    <h:body>
        <p>
            <h:form>
            <h:inputText value="#{calculationBean.num1}"/>
            <br/>
            <h:inputText value="#{ calculationBean.num2}"/>
            
            <br/><br/>
            Sum: <h:outputText id="sum" value="#{ calculationBean.sum}"/>
            
            <br/><br/>
            <h:commandButton value="Calculate" type="submit" action="#{ calculationBean.addNumbers()}">
                
            </h:commandButton>
            </h:form>
        </p>
    </h:body>
</html>

How It Works

One of the high points to using CDI is that it helps provide a seamless integration between the web views and the back-end business logic for an application. Utilizing CDI, public bean members and methods can be made accessible to JSF views very easily. The javax.inject.Named annotation provides a facility for referencing a JavaBean class from within a JSF view, either by accepting a String that will be used to make the reference or by simply utilizing the JavaBean class name with a lowercase first letter. The solutions provided within this recipe demonstrate both techniques. From a technical standpoint, Solution #2, not making use of a String to provide the reference, is the most type-safe solution. However, sometimes it is necessary to provide a String for reference, as demonstrated in Solution #1, but that solution is recommended only on an as-needed basis.

image Note   Notice that the bean in Solution #1, CalculationBean, contains an @RequestScoped annotation. This annotation specifies the scope for the bean state. For a fun trick, try to remove the @RequestScoped annotation and see what happens. As it turns out, the bean will still work as prescribed, but it will not return any results. This is because the bean will be reinitialized after each request. Therefore, the view will call the getSum method to read the current contents of the sum field, and it will have been reinitialized to a value of 0 before the request has been made. To learn more about bean scope, please see Recipe 12-4.

By annotating a class with @Named, it becomes available for use by JSF views within the same application. Any public class member or method can be called upon from within a JSF view by specifying the name of the class with a lowercase first letter, along with the public member or method that is needed. For instance, the following JSF EL expression calls upon a method named myMethod that is contained within a class named MyClass. Note that this EL expression works if the class is named MyClass and includes an empty @Named annotation and if the class is named something different and includes the @Named("myClass") annotation.

#{myClass.myMethod}

As mentioned in the sidebar for this recipe, the @ManagedBean and @Named annotations play similar roles in that they both make Java classes available for use within a web view. However, it is safe to acknowledge that the @Named annotation is preferred if using CDI; please read the note above for more information.

12-3. Allocating a Specific Bean for Injection

Problem

You have more than one JavaBean that implements a particular API, and you want to specify which of them you want to inject.

Solution

Utilize a qualifier for the injection. To alleviate the issues of referencing a duplicate class, add a qualifier to each of the classes to differentiate them from one another. In the following code example, two classes, named PaperbackController and EbookController, each implement the Book interface. To allow client bean developers the ability to specify which of the bean classes should be injected, qualifiers are used. In the first listing, let’s take a look at the Book interface, which is being implemented by at least two JavaBeans in the example.

public interface Book {
    public String title = null;
    public String description = null;
}

The class PaperbackController uses a qualifier @Paperback in order to differentiate it from other beans that implement the Book interface. The following listing is that of the PaperbackController class. Note that the Paperback interface (source shown next) must already exist in order to utilize the @Paperback annotation in this example.

package org.javaeerecipes.chapter12.recipe12_03;

import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
import java.io.Serializable;

@Named(value = "paperbackController")
@SessionScoped
@Paperback
public class PaperbackController implements Serializable, Book {

    /**
     * Creates a new instance of PaperbackController
     */
    public PaperbackController() {
    }
...
}

Another JavaBean, named EbookController, also implements the Book interface. It contains a different qualifier, @Ebook, in order to differentiate it from other classes implementing the Book interface. The EbookController class looks like the following:

package org.javaeerecipes.chapter12.recipe12_03;

import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
import java.io.Serializable;

@Named(value = "ebookController")
@SessionScoped
@Ebook
public class EbookController implements Serializable, Book {

    /**
     * Creates a new instance of EbookController
     */
    public EbookController() {

    }
...
}

Lastly, let’s see what the @Paperback and @Ebook binding annotations actually look like. The following two code listings show the contents of the org.javaeerecipes.chapter12.recipe12_03.Paperback and org.javaeerecipes.chapter12.recipe12_03.Ebook interfaces, which are used to create the two annotations:

import java.lang.annotation.*;
import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Paperback {}

import java.lang.annotation.*;
import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Ebook {}

When a client wants to make use of one or the other, it simply needs to call upon the qualifier as follows:

@Paperback PaperbackController paperback;
@Ebook EbookController ebook;

How It Works

When there are two or more classes that implement the same Java interface, CDI needs some help to determine which of them is going to be used at an injection point. If an attempt is made to deploy an application that uses CDI and an attempt is made to perform injection on a class that implements the same interface as another class, then Weld will throw an ambiguous dependency error. This means that it cannot determine what bean to use for the given injection point. When CDI attempts to determine which bean should be used at an injection point, it takes all class types into account, and it also uses qualifiers. A qualifier is an annotation that can be applied at the class level to indicate the type of a bean. Qualifiers can also be used to annotate methods, or other areas of code, to help CDI determine what kind of bean needs to be injected.

image Note   Weld is the reference implementation for CDI. Therefore, you will see references to Weld within the server logs when utilizing CDI within a Java EE application. For more information regarding Weld, please see the online documentation at http://seamframework.org/Weld.

Every bean without an explicit qualifier automatically becomes annotated with the @Default qualifier. This qualifier is not needed when another qualifier type is used. In the solution to this recipe, two qualifiers are created in order to mark two different beans of the Book type: the @Paperback and @Ebook qualifiers. To create a qualifier, generate a Java interface, and annotate that interface with @Qualifier, Retention(RetentionPolicy.RUNTIME), and @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}). All qualifiers are created in the same manner, and once created, they can be used to annotate beans for differentiation. As you can see from the example, both the PaperbackController and EbookController classes have been annotated with their respective qualifiers. This makes for an easy way to allow CDI to determine which bean to inject since each of the two beans are different implementations of the Book type.

The CDI API provides a handful of qualifiers out of the box that can be used within your bean classes. I have already discussed the @Default qualifier, which is added to any bean that does not explicitly contain a qualifier. Other qualifiers that are provided by CDI include @Named and @Any. The @Named qualifier is used to mark a bean as EL-injectable. If a bean contains an @Named qualifier, then it can be referenced within a JSF view. The @Any qualifier is also included on all beans, and it allows an injection point to refer to all beans or events of a certain bean type. For instance, to refer to all of the beans of type Book, you could declare a member as follows:

@Inject @Any Instance<Book> anyBook;

Qualifiers are not used in everyday code, but they are a feature of Java EE that come in handy on occasions where ambiguous bean injection is possible.

12-4. Determining Scope of a Bean

Problem

You want to ensure that the scope of a particular bean within your application will be available for a user’s entire session.

Solution

Define the scope of the bean that you want to make available by annotating the bean accordingly. The org.javaeerecipes.chapter12.bean.PaperbackController and org.javaeerecipes.chapter12.EbookController that are listed in Recipe 12-3 are examples of request-scoped beans since they are annotated accordingly. To make a bean available within a different scope, annotate using one of the other scope-based annotations. For example, let’s create a bean that has a session scope, meaning that it will retain its state for multiple HTTP requests. To create a session-scoped bean, annotate the class using @SessionScoped. The following class, named CartBean, is a CDI session-scoped JavaBean that contains an integer field, which will be adjusted when a user invokes either the addItem or removeItem method:

package org.javaeerecipes.chapter12.recipe12_04;

// Import and change to @RequestScoped to see a functional difference
//import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class CartBean implements java.io.Serializable {
    
        
    private int orderList = 0;
    
    public CartBean(){}
    
    public void addItem(){
        setOrderList(getOrderList() + 1);
    }
    
    public void removeItem(){
        setOrderList(getOrderList() - 1);
    }

    /**
     * @return the orderList
     */
    public int getOrderList() {
        return orderList;
    }

    /**
     * @param orderList the orderList to set
     */
    public void setOrderList(int orderList) {
        this.orderList = orderList;
    }
    
    
}

image Note   The comment within the CartBean class indicates that if you change the scope to @RequestScoped, you will see a functional difference. The difference is that the orderList field will retain its state for only one HTTP request. Therefore, the number will never increase more than 1, and it will never decrease below -1.

What fun would this bean be if you did not use it within a JSF view? Well, let’s take a look at a JSF view, named recipe12_04.xhtml, which utilizes the CartBean class to display the orderList field. The view contains two buttons, each of which is bound to different methods that reside within the CartBean class. One button will increase the size of the orderList int, and the other button will decrease it.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Recipe 12-4: Determining Scope of a Bean</title>
      
    </h:head>
    <h:body>
        <p>
            <h:form>
            <h:outputText value="#{cartBean.orderList}"/>
            <br/>
            
            <br/><br/>
            <h:commandButton value="Add Order" type="submit" action="#{cartBean.addItem()}"/>
                
            <h:commandButton value="Remove Order" type="submit" action="#{cartBean.removeItem()}"/>
            </h:form>
        </p>
    </h:body>
</html>

How It Works

Depending upon an application’s requirement, some beans may need to retain state longer than others. Sometimes it makes sense for each user of an application to have its own version of a particular bean, whereas the state of the bean lives and dies with the user’s session. Other times it makes more sense for a bean to share its state among all users of an application, and still other times it makes sense for a bean’s state to live and die with each user request. To specify the amount of time that a bean will retain its state, annotate the bean class with one of the CDI scope annotations. Table 12-1 describes the different scope annotations.

Table 12-1. CDI Bean State Annotations

Annotation Description
@RequestScoped Per user and retains state for a single HTTP request.
@SessionScoped Per user and retains state across multiple HTTP requests.
@ApplicationScoped Shared state across all user interactions within an application.
@Dependent Object exists to serve one client bean and contains the same life cycle as the bean. (This is the default scope if none specified.)
@ConversationScoped Per user scope and is utilized within a JSF application. Boundaries of the scope are controlled via a developer and extend the scope across multiple invocations of the JSF life cycle. All long-running conversations are scoped to a particular servlet session and may not cross session boundaries.

While it is easy to define a particular scope for a bean, sometimes it takes some practice and testing to determine the correct scope for a particular application requirement. Moreover, as an application evolves, it makes sense to review the different scopes that have been applied to various beans to ensure that the assigned scope is still desirable.

image Note   One of the most common mistakes when working with the scope annotations is importing the wrong annotation for use within the bean. Remember that JavaServer Faces has its own set of scope-based annotations for use within managed beans. Always be sure to import from the javax.enterprise.context.* package when working with CDI scope or you will achieve erroneous results.

12-5. Injecting Non-bean Objects

Problem

You want to inject an object that is not a bean into another Java class.

Solution #1

Use producer fields to inject objects that are not beans, objects that require custom initialization, or objects that may have varying values at runtime. To create a Producer field, annotate a public class field with the javax.injection.Produces annotation, and return the field you want to inject. In most cases, you will also need to annotate a producer method with a CDI qualifier so that CDI will know what to inject when called upon.

In this example, a JavaBean named InitalValueController contains a producer field that will be called upon to assign an initial value to CDI bean fields. The following source listing is that of the IntialValueController class, which contains the producer field implementation:

package org.javaeerecipes.chapter12.recipe12_05;

import javax.enterprise.inject.Produces;

public class InitialValueController implements java.io.Serializable {
    
       @Produces @InitValue public int initialValue = 1000;
        
        
}

The producer field in the class listing contains a qualifier annotation of @InitValue. The qualifier implementation is as follows:

package org.javaeerecipes.chapter12.recipe12_05;

import java.lang.annotation.*;
import javax.inject.Qualifier;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Qualifier
public @interface InitValue {}

The producer field can be called upon from anywhere. In this case, it is injected into a CDI bean in order to initialize a bean field value. In the following listing, the CDI bean field named ProducerExample demonstrates how to inject the producer field and make use of it:

package org.javaeerecipes.chapter12.recipe12_05;

import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@SessionScoped
public class ProducerExample implements java.io.Serializable {
    
    @Inject
    @InitValue
    private int initial;
    
    private int orderList = -1;
    
    public ProducerExample(){
        
    }
    
    public void addItem(){
        setOrderList(getOrderList() + 1);
    }
    
    public void removeItem(){
        setOrderList(getOrderList() - 1);
    }

    /**
     * @return the orderList
     */
    public int getOrderList() {
        if (orderList == -1)
            orderList = initial;
        return orderList;
    }

    /**
     * @param orderList the orderList to set
     */
    public void setOrderList(int orderList) {
        this.orderList = orderList;
    }
    
}

When the orderList field is added to a JSF view, the getOrderList method will be invoked upon the loading of the view because the orderList property is called upon from the view. This will, in turn, cause the orderList field value to become initialized the first time the JSF view is loaded. The following code demonstrates the use of the field within a JSF view. To see the sources, please look at the chapter12/recipe12_05.xhtml file.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Recipe 12-5: Injecting Non-Bean Objects</title>
      
    </h:head>
    <h:body>
        <p>
            <h:form>
            <h:outputText value="#{producerExample.orderList}"/>
            <br/>
            
            <br/><br/>
            <h:commandButton value="Add Order" type="submit" action="#{producerExample.addItem()}"/>
                
            <h:commandButton value="Remove Order" type="submit" action="#{producerExample.removeItem()}"/>
            </h:form>
        </p>
    </h:body>
</html>

How It Works

Situations may arise when it makes sense to inject an object other than a CDI managed bean or resource. Objects such as fields, methods, and the like, can become injection targets if they are declared as producers. In some cases, it may make sense to declare a class field as an injectable object. To do so, annotate the field with javax.enterprise.inject.Produces (@Produces), and the EE container will then treat the field as a getter method for the field. In most cases, a CDI qualifier annotation should also be created and used to annotate the field so that the field can be referenced via the qualifier at the injection point.

In the solution to this recipe, a field that will be used to initialize values is declared within a Java class named IntitialValueController. The field name is initialValue, and it will return an int type, being the number that will be used for initialization. Looking at the code, you can see that a qualifier named @InitValue is also placed at the field declaration. This will allow the injection point to simply refer to the qualifier to gain a handle on the injection target. To use the initialValue field, it is injected into a CDI managed bean as follows:

@Inject
@InitValue
private int initial;

One injected, the field can be utilized as if it were part of the class into which it was injected. In the case of this example, it is used to initialize the value of the orderList field, which is then displayed via a JSF view named chapter12/recipe12_05.xhtml.

It is also possible to create producer methods, which can return values that are injectable to a bean or non-Java (JSF) context. In doing so, the @Produces annotation is used to annotate the method in the same manner that a field producer is declared. For example, the following method demonstrates the declaration of a producer method that would be used to inject an object of the Book type. The method can be called upon in order to return the desired Book object type, depending upon the type that is passed to it.

@Produces @BookQualifier public Book getBook(Book book){
        
    if(book.equals(EbookController.class))
        return new EbookController();
    else
        return new PaperbackController();
}

In this case, the method also uses a qualifier named @BookQualifier. The producer method result can then be injected into a bean or non-Java context. The injection point references the qualifier in order to make the injection possible, and the producer method is called by the container to obtain the desired instance object as follows:

@Inject
@BookQualifier
Book getBook(ebookController);

Producers can be a great way to develop injectable objects. With a bit of practice, they can also become valuable for creating sophisticated object factories via the use of a producer method.

12-6. Ignoring Classes

Problem

You want to mark a class as ignored by CDI.

Solution #1

Denote the class with the @Veto annotation. Any class containing the @Veto annotation will be ignored by CDI. The following example demonstrates the use of @Veto:

@Veto
public class OrderBean implements java.io.Serializable {
    
    public OrderBean(){
        
    }
    
    // Some Class Implementation
}

Solution #2

Denote the class with the @Requires annotation to mark the class as ignored by CDI if it does not meet the specified requirements. The following example demonstrates how to utilize the @Requires annotation:

@Requires("javax.persistence.EntityManager")
public class EmployeeFacade {
    ...
    @Produces
    public EntityManager getEntityManager(){
        ...
    }
    ...

}

In this example, the @Requires annotation has a String containing javax.persistence.EntityManager passed to it. As such, if the specified class is not available and/or the class is unable to fulfill the specified dependency, then it will be ignored by CDI.

How It Works

To veto a bean means to mark it as ignored by CDI. Therefore, if a bean contains the @Veto annotation, it cannot be processed by CDI. A vetoed class will not contain the life cycle of a contextual instance, and it cannot be injected into other classes. In fact, if a managed bean or session bean contains the @Veto annotation, it cannot be considered a managed bean or session bean at all. In some cases, it makes sense to mark a bean as such to ensure that it cannot become managed by CDI. The following code demonstrates how to apply the @Veto annotation to a class.

The @Veto annotation can also be placed on a package declaration, which will prevent all of the beans that are contained within that package from being processed via CDI.

@Veto
package org.javaee7recipes.chapter12.*;
...

Any of the following definitions on a vetoed type will not be processed:

  • Managed beans, session beans, interceptors, decorators
  • Observer methods, producer methods, producer fields

The @Requires annotation can be used to mark a class to be ignored by CDI if it does not meet the specified required criteria. The @Requires annotation accepts a String-based fully qualified class name of the dependency or dependencies. If the object is able to fulfill its dependencies, then it will be managed by CDI. Similarly to @Veto, the @Requires annotation can be placed on a package as well. If that package is unable to fulfill the dependency that is denoted by @Requires, then all classes contained within that package will be unmanaged by CDI.

12-7. Disposing of Producer Fields

Problem

Your application uses a producer field, and you want that field to be destroyed once it is no longer required for use.

Solution

Mark the producer field with the @Disposes annotation to indicate that it should be removed once it is no longer in use. The following code excerpt demonstrates a producer field that will be removed once it is no longer required for use:

...
    @Produces @Disposer
    List<Book> books;
...

How It Works

A producer method can be used to generate an object that needs to be removed once it is no longer needed. Much like a finalizer for a class, an object that has been injected via a producer method can contain a method that is invoked when the injected instance is being destroyed. Such a method is known as a disposer method. To declare a method as a disposer method, create a method defined by the same class as the producer method. The disposer method must have at least one parameter, with the same type and qualifiers as the producer method. That parameter should be annotated with @Disposes. In CDI 1.1, this technique can now be applied to producer fields.

12-8. Specifying an Alternative Implementation at Deployment Time

Problem

You want to have the ability to code different implementations of an interface and then choose which implementation to utilize when an application is deployed.

Solution

Create a default implementation for an interface, and then create any alternative implementations for that interface and denote them with the @Alternative annotation. Specifying the javax.enterprise.inject.Alternative annotation flags a class as an alternative, and if that class is noted in the beans.xml file, then it will be loaded at deployment time, rather than the default interface implementation.

The following code excerpt demonstrates the use of an alternative class implementation. For the purposes of this demonstration, let’s assume that there is already a default implementation for the OrderType interface named BookstoreOrderBean.

@Alternative
public class WarehouseOrderBean implements OrderType {
...
}

To specify the use of the alternative implementation rather than the default, modify the beans.xml file accordingly. The following is an example excerpt from the beans.xml file that designates the use of the WarehouseOrderBean:

<beans ... >
    <alternatives>
        <class>org.javaeerecipes.chapter12.WarehouseOrderBean</class>
    </alternatives>
</beans>

How It Works

Sometimes it makes sense to create two or more implementations of a class for use in different environments. However, it can become a cumbersome nightmare to rename classes, and so on, in order to build and distribute the correct implementation for each environment. The use of the javax.enterprise.inject.Alternative annotation allows more than one implementation of an interface to be used, and the appropriate implementation can be specified by altering the file before deployment.

12-9. Injecting Bean Metadata

Problem

You want to acquire metadata information about a bean from within your application classes.

Solution

Inject the interface of a bean into the classes that need to utilize the metadata. Once it’s injected, call upon the bean methods to retrieve the required metadata. In the following example, a bean named OrderBean has its metadata injected:

@Named("OrderBean")
public class Otherbean {
    @Inject Bean<Order> bean;

        public String getBeanName(){
        return bean.getName();
    }
    
    public Class<? extends Annotation> getBeanScope(){
        return bean.getScope();
    }
}

How It Works

If you need to use bean metadata, you can easily obtain it by injecting the target bean’s metadata. To do so, specify the @Inject annotation, followed by the Bean class of the target bean type. Once the bean interface has been injected, methods can be called upon it to obtain the desired information. Table 12-2 describes the different methods that can be called upon the Bean class to obtain metadata.

Table 12-2. Bean Metadata

Method Description
getName Returns the name of the bean
getBeanClass Returns the bean class
getInjectionPoints Returns a Set of InjectionPoint objects for the bean
getQualifiers Returns a Set of qualifier annotations for the bean
getScope Returns the scope of the bean
getStereotypes Returns a Set of stereotype data (common metadata) for a bean
getTypes Returns a Set of the bean types
isAlternative Returns a Boolean to specify whether the bean is an alternative
isNullable Returns a Boolean to specify whether a bean can be nullable
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.80.101