Auxiliary Tools :: Introduction to Contexts and Dependency Injection (CDI) – Part I

 

in progress

About CDI
CDI 1.2 is a specification designed to enable Enterprise Java Beans (EJB) 3 components to be used as Java Server Faces (JSF) managed beans. CDI unifies the two component models and enables a considerable simplification to the programming model for web-based applications in Java. CDI enables your objects to have their dependencies provided to them automatically, instead of creating them or receiving them as parameters. CDI also manages the lifecycle of those dependencies for you.
For example, consider the following servlet:

This servlet needs an instance of an object that implements the Message interface:

The servlet creates itself an instance of the following object:

Using CDI, this servlet can declare its dependency on a Message instance and have it injected automatically by the CDI runtime. The new servlet code is the following:

The CDI runtime looks for classes that implement the Message interface, finds the MessageB class, creates a new instance of it, and injects it into the servlet at runtime. To manage the lifecycle of the new instance, the CDI runtime needs to know what the scope of the instance should be. In this example, the servlet only needs the instance to process an HTTP request; the instance can then be garbage collected. This is specified using the javax.enterprise.context.RequestScoped annotation:

The benefits of CDI include:

  • Simplifying and shrinking your code base by replacing big chunks of code with annotations.
  • Flexibility, allowing you to disable and enable injections and events, use alternative beans, and inject non-CDI objects easily.
  • Optionally, allowing you to include beans.xml in your META-INF/ or WEB-INF/ directory if you need to customize the configuration to differ from the default. The file can be empty.
  • Simplifying packaging and deployments and reducing the amount of XML you need to add to your deployments.
  • Providing lifecycle management via contexts. You can tie injections to requests, sessions, conversations, or custom contexts.
  • Providing type-safe dependency injection, which is safer and easier to debug than string-based injection.
  • Decoupling interceptors from beans.
  • Providing complex event notification.

Bean Discovery
Bean discovery is defined for each module (e.g. JAR) of the application, also called “bean archive” and can have 3 modes:

  • Annotated (default mode when no beans.xml file is present): only classes having specific annotations called Bean Defining Annotations will be discovered
  • All: all of the classes will be discovered
  • None: none of the classes will be discovered

Keep in mind that there is no global configuration for bean discovery, it is set only for the current bean archive.
Example of a beans.xml file setting bean discovery mode to all:

If the bean discovery mode is annotated then:

  • Bean classes that do not have bean defining annotation and are not bean classes of sessions beans are not discovered.
  • Producer methods that are not on a session bean and whose bean class does not have a bean defining annotation are not discovered.
  • Producer fields that are not on a session bean and whose bean class does not have a bean defining annotation are not discovered.
  • Disposer methods that are not on a session bean and whose bean class does not have a bean defining annotation are not discovered.
  • Observer methods that are not on a session bean and whose bean class does not have a bean defining annotation are not discovered.

Bean Defining Annotations
When a bean archive has its bean discovery mode set to Annotated (the default mode when no beans.xml is present), only the types with one of these annotations will be discovered:

  • @ApplicationScoped, @SessionScoped, @ConversationScoped and @RequestScoped annotations
  • All other normal scope types
  • @Interceptor and @Decorator annotations
  • All stereotype annotations, i.e. annotations annotated with @Stereotype
  • The @Dependent scope annotation

If one of these annotations is declared on a bean class, then the bean class is said to have a bean defining annotation.
Note that EJB session beans are an exception to the bean discovery mechanism as they are always discovered as CDI beans (unless explicitly excluded.)

Exclude Beans From the Scanning Process (Exclude filters)
Exclude filters are defined by elements in the beans.xml for the bean archive as children of the element. By default an exclude filter is active. If the exclude filter definition contains:

  • a child element named with a name attribute, and the classloader for the bean archive can not load a class for that name, or
  • a child element named with a name attribute, and the classloader for the bean archive can load a class for that name, or
  • a child element named with a name attribute, and there is no system property defined for that name, or
  • a child element named with a name attribute and a value attribute, and there is no system property defined for that name with that value.

then the filter is inactive.

If the filter is active, and:

  • the fully qualified name of the type being discovered matches the value of the name attribute of the exclude filter, or
  • the package name of the type being discovered matches the value of the name attribute with a suffix “.*” of the exclude filter, or
  • the package name of the type being discovered starts with the value of the name attribute with a suffix “.**” of the exclude filter

then we say that the type is excluded from discovery.

For example, consider the follow beans.xml file:

The first exclude filter will exclude all classes in com.acme.rest package. The second exclude filter will exclude all classes in the com.acme.faces package, and any subpackages, but only if JSF is not available. The third exclude filter will exclude all classes in the com.acme.verbose package if the system property verbosity has the value low. The fourth exclude filter will exclude all classes in the com.acme.ejb package, and any subpackages if the system property exclude-ejbs is set (with any value) and at the same time, the javax.enterprise.inject.Model class is available to the classloader.

The CDI Container
The container is the heart of CDI: you can think of it as the invisible conductor of your application.
It checks all possible CDI code at boot time, so exceptions at runtime are very rare in CDI–you know that something is wrong in your code at launch.
The container manages your components’ lifecycle and services. It’ll create class instances for you when needed and add CDI features on the provided object. This enriched object will be automatically destroyed when the scope it is bound to is destroyed.
That’s why you’ll never use the “new” operator on a bean class unless you want to forego all CDI features on the resulting instance.

Beans and Contextual Instances
CDI, at the most basic level, revolves around the notion of beans. The container discovers them at startup time by scanning classes in the deployment. A bean is defined by a set of attributes obtained by reading annotations and type on the bean definition. As we said above, the CDI container is in charge of creating and destroying bean instances according to their context, hence the term contextual instance. The table below introduces these attributes–they’ll be detailed later in this Refcard.

Table. Beans attributes

Types set The set of Java types that the bean provides. This set is used when performing typesafe resolution (find the candidate bean for an injection point).
QualifiersDeveloper-defined annotations . p rovide a typesafe way to distinguish between multiple beans sharing the same type. They are also used by the typesafe resolution mechanism.
ScopeAlso known as “context”. Determines the lifecycle and visibility of a bean. The container uses this attribute to know when to create and destroy a bean instance.
Alternative statusA bean can be defined as an alternative for an other bean. This feature can be used to simplify test creation, for instance.
NameThis optional value, is the only way to resolve a bean in a non typesafe way (i.e. with a String identifier). It allows bean access from the UI layer (JSF or JSP) or when integrating a legacy framework with CDI.

Bean vs. Contextual Instances
In a lot of blog posts and documentation, the term Bean is often used instead of contextual instance. It’s important to understand the difference. A bean is a collection of metadata associated with some code (usually a class) used by the container to provide a contextual instance. A contextual instance is the object that the container creates from the Bean attributes when an injection point has to be satisfied. In short, unless you’re developing an advanced CDI feature, your code will only deal with contextual instances at runtime.

Defining an Injection Point
As we just said, contextual instances are created and managed by the CDI container. When creating such an instance, the container may perform injection of other instances in it if it has one or more injection point. Keep in mind that injection occurs only when the instance is created by the container. Injection points are declared using the @javax.inject.Inject annotation. @Inject can be used in 3 places:

1. Field Injection
When a field is annotated with @Inject, the container will look for a bean with a matching type and will provide a contextual instance of this bean to set the field value.

2. Constructor injection
Only one constructor in a bean class may be annotated with @Inject. All parameters of the constructor will be resolved by the container to invoke it.

3. Method injection
A bean class can one or more methods annotated with @Inject. These methods are called initializer methods.

Other injection points
Two specific CDI elements always have injection point without the need of being annotated with @Inject:

  • Producer methods
  • Observer methods

Ambiguous or Unsatisfied Dependencies
An ambiguous dependency occurs when there is more than a single bean eligible for injection into an injection point. This can be resolved with the help of the following steps:

  • Add a new @Qualifier to disambiguate between the bean implementations that are eligible for injection.
  • Mark all but one of the bean implementations with @Alternative, thus disabling them by default.
  • Relocate the bean implementations into a separate bean archive than is not on the classpath of the injection point.
  • With beans.xml, disable all but one @Alternative bean that are eligible for injection to that injection point.

An unsatisfied dependency occurs when there is not a single bean that is eligible for injection into an injection point. This can be resolved as follows:

  • By creating a bean that implements the bean type of the injection point and declares all the qualifier annotations present at the injection point
  • If we already have a bean of the correct bean type and all the qualifier annotations in our application, check whether the bean is on the classpath of the bean archive that contains the injection point with the error
  • With beans.xml, enable an @Alternative bean of the correct bean type and qualifier annotations

In the event of an ambiguous or unsatisfied dependency, the container aborts deployment and throws an exception.

Qualifiers
Imagine a classic scenario – a set of beans that implements the same bean type, and injection points for those beans. Usually, when bean type has multiple implementations, you deal with a Java interface type, like this one – a simple interface representing a tennis racquet type:

More, let us develop two simple implementations of this interface. You have a Head power racquet:

Moreover, a Babolat spin racquet:

At this moment, you can inject these implementations into a bean (@Inject), but an ambiguous injection error will occur, because the container will not know which implementation must be injected. Qualifiers will save us, because they will uniquely identify the implementations, therefore the container will know exactly what to inject. You can use this to define the below two qualifiers:

Now, you should annotate the two implementations accordingly, as below – at this point, the container is able to distinguish between the two implementations:

Next, you can give a test to the container, by injecting the two implementations into another bean, like this – if everything works fine, you will not get errors, and you will inject the correct information – this is the prove that the container is capable to distinguish between the Head and Babolat racquets:

Call the racquetSpin and racquetPower property from a JSF page through EL (notice that the @Named built-in qualifier let JSF to do this):

@Any

Whenever a bean or injection point does not explicitly declare a qualifier, the container assumes the qualifier @Default. From time to time, you will need to declare an injection point without specifying a qualifier. There is a qualifier for that too. All beans have the qualifier @Any. Therefore, by explicitly specifying @Any at an injection point, you suppress the default qualifier, without otherwise restricting the beans that are eligible for injection.

This is especially useful if you want to iterate over all beans with a certain bean type.

Every bean has the qualifier @Any, even if it does not explicitly declare this qualifier.

Every event also has the qualifier @Any, even if it was raised without explicit declaration of this qualifier.

The @Any qualifier allows an injection point to refer to all beans or all events of a certain bean type.

Built-in qualifiers

  • @Named – set bean name for weak typed environment (EL, Javascript)
  • @Default – added to all beans without other qualifiers, or having only the @Named qualifier
  • @Any – added to all beans for programmatic lookup and decorators
  • @Initialized – to qualify events when a context is started
  • @Destroyed – to qualify events when a context is destroyed

Managed Beans
Java EE establishes a common definition in the Managed Beans specification. Managed Beans are defined as container-managed objects with minimal programming restrictions, otherwise known by the acronym POJO (Plain Old Java Object). They support a small set of basic services, such as resource injection, lifecycle callbacks, and interceptors. Companion specifications, such as EJB and CDI, build on this basic model.

With very few exceptions, almost every concrete Java class that has a constructor with no parameters (or a constructor designated with the annotation @Inject) is a bean. This includes every JavaBean and every EJB session bean.

Types of Classes That are Beans
A managed bean is a Java class. The basic lifecycle and semantics of a managed bean are defined by the Managed Beans specification. You can explicitly declare a managed bean by annotating the bean class @ManagedBean, but in CDI you do not need to. According to the specification, the CDI container treats any class that satisfies the following conditions as a managed bean:

  • It is not a non-static 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 does not implement interface javax.enterprise.inject.spi.Extension.
  • It has either a constructor with no parameters, or a constructor annotated with @Inject.
  • It is not annotated @Vetoed or in a package annotated @Vetoed.

The unrestricted set of bean types for a managed bean contains the bean class, every superclass and all interfaces it implements directly or indirectly.
If a managed bean has a public field, it must have the default scope @Dependent.

@Vetoed
CDI 1.1 introduces a new annotation, @Vetoed. You can prevent a bean from injection by adding this annotation:

In this code, the SimpleGreeting bean is not considered for injection.

All beans in a package may be prevented from injection:

This code in package-info.java in the org.sample.beans package will prevent all beans inside this package from injection.
Java EE components, such as stateless EJBs or JAX-RS resource endpoints, can be marked with @Vetoed to prevent them from being considered beans. Adding the @Vetoed annotation to all persistent entities prevents the BeanManager from managing an entity as a CDI Bean. When an entity is annotated @Vetoed, no injections take place. The reasoning behind this is to prevent the BeanManager from performing the operations that may cause the JPA provider to break.

Use CDI to Inject an Object Into a Bean
CDI is activated automatically if CDI components are detected in an application. If you wish to customize your configuration to differ from the default, you can include META-INF/beans.xml or WEB-INF/beans.xml to your deployment archive.

Inject Objects into Other Objects
1. To obtain an instance of a class, annotate the field with @Inject within your bean:

2. Use your injected object’s methods directly. Assume that TextTranslator has a method translate:

3. Use an injection in the constructor of a bean. You can inject objects into the constructor of a bean as an alternative to using a factory or service locator to create them:

4. Use the Instance() interface to get instances programmatically. The Instance interface can return an instance of TextTranslator when parameterized with the bean type.

When you inject an object into a bean, all of the object’s methods and properties are available to your bean. If you inject into your bean’s constructor, instances of the injected objects are created when your bean’s constructor is called, unless the injection refers to an instance that already exists. For instance, a new instance would not be created if you inject a session-scoped bean during the lifetime of the session.

EJB
EJBs predate CDI beans and are in someways similar to CDI beans and in other ways very different. Primarily, the differences between CDI beans and EJBs is that EJBs are :

  • Transactional
  • Remote or local
  • Able to passivate stateful beans freeing up resources
  • Able to make use of timers
  • Can be asynchronous

The two types of EJBs are called stateless and stateful. Stateless EJBs can be thought of as thread safe single-use beans that don’t maintain any state between two web requests. Stateful EJBs do hold state and can be created and sit around for as long as they are needed until they are disposed of.

Defining an EJB is simple, you just add either a javax.ejb.Stateless or javax.ejb.Stateful annotation to the class.

Stateless beans must have a dependent scope while a stateful session bean can have any scope. By default they are transactional, but you can use the transaction attribute annotation.

While EJBs and CDI beans are very different in terms of feaures, writing the code to integrate them is very similar since CDI beans can be injected into EJBs and EJBs can be injected into CDI beans. There is no need to make any distinction when injecting one into the other. Again, the different scopes are handled by CDI through the use of proxying. One exception to this is that CDI does not support the injection of remote EJBs but that can be implemented by writing a simple producer method for it.

The javax.inject.Named annotation as well as any Qualifiers can be used on an EJB to match it to an injection point.

When to use which bean
How do you know when to use which bean? Simple.

Never use JSF managed beans unless you are working in a servlet container and don’t want to try and get CDI working in Tomcat (although I have a Maven archetype for that so there’s no excuse).

In general, you should use CDI beans unless you need the advanced functionality available in the EJBs such as transactional functions. You can write your own interceptor to make CDI beans transactional, but for now, its simpler to use an EJB until CDI gets transactional CDI beans which is just around the corner. If you are stuck in a servlet container and are using CDI, then either hand written transactions or your own transaction interceptor is the only option without EJBs.

Contexts and Scopes
A context, in terms of CDI, is a storage area that holds instances of beans associated with a specific scope.

A scope is the link between a bean and a context. A scope/context combination may have a specific lifecycle. Several predefined scopes exist, and you can create your own. Examples of predefined scopes are @RequestScoped, @SessionScoped, and @ConversationScope.

Table. Available Scopes

ScopeDescription
@DependentThe bean is bound to the lifecycle of the bean holding the reference. The default scope for an injected bean is @Dependent.
@ApplicationScopedThe bean is bound to the lifecycle of the application.
@RequestScoped@RequestScoped
@SessionScopedThe bean is bound to the lifecycle of the session.
@ConversationScopedThe bean is bound to the lifecycle of the conversation. The conversation scope is between the lengths of the request and the session, and is controlled by the application.
Custom scopes If the above contexts do not meet your needs, you can define custom scopes.

Named Beans
Use the @Named annotation to assign a name to a bean:

Use the named bean (the above) in a JSF view.

Managed Bean Lifecycles
The default scope for an injected bean is @Dependent. This means that the bean’s lifecycle is dependent upon the lifecycle of the bean that holds the reference. Several other scopes exist, and you can define your own scopes.

Annotate the bean with the desired scope.

When your bean is used in the JSF view, it holds state.

 

Leave a Reply