CDI: Context and Dependency Injection (JSR-299)

 

Introduction

Fundamental Services

* Contexts
– Bind stateful components to lifecycle contexts (i.e. client does not control bean’s lifecycle, container does)
– Lifecycle contexts are well defined and extensible
* Dependency injection
– Inject components into an application in a typesafe way
– Deployment time decision on which implementation of a particular interface to inject

Other Services

* EL (expression language) integration:
– Components can be used directly on JSP/JSF pages
* Decorate injected components
* Associate interceptors with components using typesafe interceptor bindings
* Event notification model
* Added web conversation scope (in addition to request, session, and application)
* Complete SPI spec:
– Clean third party integration

Loose Coupling

* Server/client decoupling via
– Well-defined types and qualifiers (so that server implementation may vary)
* Component lifecycle decoupling via
– Adding context to components
– Automatic life cycle management for components
– Allowing stateful components to interact like services (purely by message passing)
* Message producer/consumer decoupling via events
* Orthogonal concerns decoupling via JEE interceptors

Strong Typing Benefits

* No lookups using string-based names for wiring and correlations (errors can be detected at compile time)
* Allow Java annotation

CDI Beans

* Beans are stateful whose lifecycles are managed by containers, not by bean clients.

Bean Attributes

* Bean Types
– Almost any Java type may be a bean type
* Qualifiers
* Scope
* EL name
* Interceptor bindings
* Implementation

What is a Managed Bean

A managed bean is a top-level Java class if
* Defined to be a managed bean by, for example, JSF spec.
* Or meets all the following 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:
1) a constructor with no parameters
2) a constructor annotated @Inject

Injectable Objects

* Any Java class (almost: what’s not?)
* Session Beans
* JEE resources: data sources, JMS topics/queues/connection factories
* Persistence contexts (JPA EntityManager objects)
* Producer fields
* Objects returned by producer methods
* Web service references
* Remote EJB references

Alternatives

* Annotate a bean implementation as Alternative

public @Alternative
class MockPaymentProcessor extends PaymentProcessorImpl { ... }

* Specify to use alternative implementation in beans.xml

<beans
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 
   <alternatives>
         <class>org.mycompany.mock.MockPaymentProcessor</class>
   </alternatives>
 
</beans>

Interceptors

* An interceptor binding type is a user-defined annotation that is itself annotated @InterceptorBinding.
* It lets us bind interceptor classes to bean classes with no direct dependency between the two classes.
* Interceptors are disabled by default. Use beans.xml to enable interceptor and order interceptors.
* Define an interceptor binding type:

@InterceptorBinding
@Inherited
@Target( { TYPE, METHOD })
@Retention(RUNTIME)
public @interface Transactional {}

* Implement the interceptor binding type:

public @Transactional @Interceptor
class TransactionInterceptor { ... }

* Apply the interceptor to a bean:

public @SessionScoped @Transactional
class ShoppingCart implements Serializable { ... }

Using Qualifiers

* A qualifier is an annotation that you apply to a bean
* A qualifier type is a Java annotation defined as
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
* Used to provide various implementations of a particular bean type.
* All non qualifier annotated class are automatically annotated with @Default
* Example:

package greetings;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.*;
import javax.inject.Qualifier;
 
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Informal {}

* Use the new Qualifier:

package greetings;
@Informal
public class InformalGreeting extends Greeting {
public String greet(String name) {
return "Hi, " + name + "!";
}
}

Using Qualifier with Members

* Define a qualifier with member, e.g. TestUser :

@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD, PARAMETER })
public @interface TestUser {
	UserId userId() default UserId.user1;	
}

* Use UserId enum class to define available user ids:

public enum UserId {
	user1,
	user2;
}

* User Provider class to produce test User instances:

public class TestUserProvider {
 
	@Produces @TestUser(userId=UserId.user1)
	public User getUser1(){
                User user = new User("user1");
                return user;
        }
 
	@Produces @TestUser(userId=UserId.user2)
	public User getUser1(){
                User user = new User("user2");
                return user;
        }

* Inject User instance in classes:

	@Inject
	@TestUser(userId = UserId.user1)
	User user;

Injecting Beans

* Inject a @Default Greeting object:

import javax.inject.Inject; 
public class Printer { 
  @Inject Greeting greeting; 
  ...
}

* Inject a @Informal Greeting object:

import javax.inject.Inject; 
public class Printer { 
  @Inject @Informal Greeting greeting; 
  ...
}

Using Scopes

* CDI introduces two additional scopes, i.e. Dependent and Conversion:

@RequestScoped
@SessionScoped
@ApplicationScoped
@Dependent
@ConversationScoped

* Example:

import javax.inject.Inject; 
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
 
@Named("printer")
@RequestScoped
public class Printer { 
  @Inject @Informal Greeting greeting; 
  ...
}

Producer Methods

* Producer methods provide a way to inject objects that
– are not beans (e.g. a number)
– whose values may vary at runtime
– that require custom initialization (let application take full control of bean instantiation process)
* Example:
– Define producer method:

private int maxNumber = 100;
...
@Produces @MaxNumber int getMaxNumber() {
  return maxNumber;
}

– Inject in another managed bean will initialize maxNumber to 100:

@Inject @MaxNumber private int maxNumber;

Obtain Bean Programmatically

* Get a bean factory:

@Inject Instance<PaymentProcessor> paymentProcessorSource;

* Get a bean factory using qualifier:

@Inject @Asynchronous Instance<PaymentProcessor> paymentProcessorSource;

* Get the bean

PaymentProcessor p = paymentProcessorSource.get();

InjectionPoint object

* Used to access metadata relating to the injection point to which it belongs
* Example
– Declare a LogFactory class to produce a Logger object with appropriate class name:

public class LogFactory implements Serializable {
 
	private static final long serialVersionUID = 5266112323136335431L;
 
	@Produces
	Logger createLogger(InjectionPoint injectionPoint) {
		String name = injectionPoint.getMember().getDeclaringClass()
				.getName();
		Logger log = Logger.getLogger(name);
 
		return log;
	}
 
}

– Use following in code to automatically obtain class name:

@Inject Logger log;

– Above is equivalent to:

Logger log = Logger.getLogger(MyClass.class.getName());

– For example,

import mil.osd.dmdc.diss.test.util.WeldJUnit4Runner;
 
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
 
@RunWith(WeldJUnit4Runner.class)
public class TestLogger {
	@Inject
	Logger log;
 
         // Note that TestLogger2 has to be injected.
         // In another word this won't work: TestLogger2 l2 = new TestLogger2(); l2.testLog(); 
	@Inject
	TestLogger2 l2;
 
	@Test
	public void testLogger() {
		log.debug("Test logger...");
	}
 
	@Test
	public void testLogger2() {
		l2.testLog();
	}
}
import javax.inject.Inject;
import org.apache.log4j.Logger;
 
public class TestLogger2 {
	@Inject	Logger log;
 
	public void testLog(){
		log.debug("TestLogger2...");
	}
}

Configure CDI App

* Must have beans.xml in
– META-INF (EJB modules or JAR files)
– or WEB-INF (web app): avoid this.
– or WEB-INF/classes/META-INF (web app)

References

* The Java EE 6 Tutorial: Basic Concepts, Fourth Edition By: Eric Jendrock et al Chapter 17
* Weld – JSR-299 Reference Implementation
* To inject or not to inject: CDI is the question
* Dependency Injection – An Introductory Tutorial – Part 1

This entry was posted in jsf. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

This site uses Akismet to reduce spam. Learn how your comment data is processed.