Introduction to CDI

Me, Myself and I

  • Antoine Sabot-Durand
  • Red Hat
  • CDI spec lead
  • @antoine_sd
  • next-presso.com
  • github.com/antoinesd

Agenda

Slides here j.mp/introcdi
  • Meet CDI
  • Basic DI
  • Qualifiers
  • Producers
  • Bean resolution
  • Programmatic Lookup
  • Contexts
  • Events
  • Interceptors and Decorators
  • Conclusion

Meet CDI

Contexts and Dependency Injection

What is CDI?

cdi icon
  • A Java EE spec.
  • ASL2 (even TCK)
  • Launched in Java EE 6
  • 2 releases since launch
  • 1.2 is last (Java EE 7)
  • 2.0 on its way
  • 2 major impl.
    • JBoss Weld (RI)
    • Apache OpenWebBeans
Notes

Java SE is supported by impls and will be at spec level in 2.0

What’s included?

CDI Provides:

cdi logo
  1. A powerful programming model
  2. Different Lifecycles for stateful objects
  3. A Typesafe DI mechanism
  4. The ability to decorate injected objects
  5. The ability to intercept methods
  6. An event notification model
  7. A SPI allowing portable extensions to integrate 3rd party tech

CDI explained to your boss

CDI brings a powerful and modern programming model that standardizes good practices.

CDI provides a consistent and seamless way to integrate 3rd party frameworks to this model.

Once adopted, CDI will become the invisible backbone of our projects.

One container to rule them all

The container is the heart of CDI
The container checks all possible CDI code at boot time
The container manages your components lifecycle and services
Yet, you’ll never see it (except in advanced development)

Container is your application’s invisible conductor

Basic DI

Almost Everything is a bean

During boot time, the container scans each class to check if it meets the conditions to be a bean
These conditions are very loose so, most pojo classes are beans
public class HelloService { (1)
    public String hello() {
        return "Hello World!";
    }
}
1HelloService is a concrete class with a default constructor, therefore it’s a bean

Injection in bean field

A bean can inject other beans or be injected into other beans
public class MyBean {
    
    @Inject (1)
    private HelloService service; (2)

    public void displayHello() {
        display(service.hello();
    }
}
1@Inject annotation defines an injection point (here a field)
2The container looksa for the bean to inject by its type (here HelloService)

Injection in bean constructor

To become a bean a class must have a constructor with no parameters or a constructor annotated @Inject
public class MyBean {

    private HelloService service;

    @Inject
    private MyBean(HelloService service) {
        this.service = service;
    }
}
Only one constructor can be annotated with @Inject

Injection in a method

Such method is called initializer methods
public class MyBean {

    private HelloService service;

    @Inject
    public void initService(HelloService service) {
        this.service = service;
    }
}
all initializer methods are called at bean instantiation

One more thing…​

A bean support standard lifecycle callbacks
public class MyBean {
    @Inject
    private HelloService service;

    @PostConstruct (1)
    public void init() { ... }

    @PreDestroy (2)
    public void bye() { ... }
}
1@PostConstruct is called after all injections are done
2@PreDestroy is called before destroying the instance

Other Injection Targets In Java EE

EJB session beans, Interceptors and Decorators are also CDI beans
You can create your own injection target with CDI SPI
A lot of Java EE Components support @Inject:
  • Servlets
  • Servlet Filters
  • Event Listeners
  • JAX-RS components
  • Websocket Endpoints
  • JPA Entity Listeners

There could be only one

  • Each injection point is checked at boot time:
If no bean is eligible for it, the injection point is unsatisfied
If multiple beans are eligible for it, the injection point is ambiguous
In both case a DeploymentException is thrown by the container

Qualifiers

Solving ambiguous cases with

When an injection point resolves to multiple beans…

public class MyBean {
    @Inject
    HelloService service;
}

public interface HelloService {
    public String hello();
}

public class FrenchHelloService implements HelloService {
    public String hello() { 
        return "Bonjour tout le monde!";
    }
}

public class EnglishHelloService implements HelloService {
    public String hello() {
        return "Hello World!";
    }
}
deployment fails with an Ambiguous dependencies error.

The solution is to create Qualifiers…

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface French {
}

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface English {
}
Qualifiers are annotations annotated with @Qualifier

to qualify beans sharing the same type…​

@French
public class FrenchHelloService implements HelloService {
    public String hello() {
        return "Bonjour tout le monde!";
    }
}

@English
public class EnglishHelloService implements HelloService {
    public String hello() {
        return "Hello World!";
    }
}

and distinguish them at injection points.

public class MyBean {
    @Inject
    @French
    HelloService serviceFr;

    @Inject
    @English
    HelloService serviceEn;
    
}
Qualifiers are types enhancing injection, yet keeping it strong typed

Qualifiers can have members

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface Language {

    LangChoice value();

    @Nonbinding (1)
    String description() default "";

    public enum LangChoice {
        FRENCH, ENGLISH
    }
}
1@Nonbinding member’s value isn’t used in bean resolution mechanism

Binding members limit the number of annotations to add to your code

@Language(FRENCH)
public class FrenchHelloService implements HelloService {
    public String hello() { 
        return "Bonjour tout le monde!";
    }
}

@Language(ENGLISH)
public class EnglishHelloService implements HelloService {
    public String hello() {
        return "Hello World!";
    }
}

public class MyBean {
    @Inject
    @Language(value = FRENCH, description = "ici on parle français")
    HelloService serviceFr;

    @Inject
    @Language(value = ENGLISH, description = "english spoken here")
    HelloService serviceEn;
}

Multiple qualifiers

A bean can have multiple qualifiers
@Language(FRENCH) @Console (1)
public class FrenchHelloService implements HelloService {
    public String hello() { 
        return "Bonjour tout le monde!";
    }
}

public class MyBean {
    @Inject
    @Language(FRENCH) @Console
    HelloService serviceFr;
}
1@Console is a qualifier

Multiple qualifiers

Injection point can have a non empty subset of the bean’s qualifiers
@Language(FRENCH) @Console
public class FrenchHelloService implements HelloService {
    public String hello() { 
        return "Bonjour tout le monde!";
    }
}

public class MyBean {    
    @Inject
    @Console
    HelloService serviceFr;
}

Multiple qualifiers

Injection point can’t have a super set of bean’s qualifier
@Language(FRENCH) @Console
public class FrenchHelloService implements HelloService {
    public String hello() { 
        return "Bonjour tout le monde!";
    }
}

public class MyBean { 
    @Inject
    @Language(FRENCH) @Console @Language(CANADIAN) (1)
    HelloService serviceFr;
}
1Unsatisfied injection point: deployment fails

Built-in qualifiers

@Named set bean name for weak typed environment (EL, Javascript)
@Default added to beans without qualifier or having only @Named
@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

Examples

public class MyBean { ... } (1)
    
@Named
public class MyBean2 { ... } (2)

@Named @Language(FRENCH) (3)
public class MyBean2 {
    @Inject (4)
    MyBean2 bean;
}
1this bean has @Default and @Any qualifiers
2this bean has @Default, @Named and @Any qualifiers
3this bean has @Language(FRENCH), @Named and @Any qualifiers
4this injection point has @Default qualifier

Producers

When you don’t own your bean’s class, use

What is a producer?

  • A way to declare a bean…​
  • …​from a field or a method…​
  • …​for a class you don’t own…​
  • …​or a non CDI class.

Declaring a Producer

public class ProducingBean { (1)
  @Produces (2)
  private List<Integer> mapInt = new ArrayList<>();

  @Produces (2)
  @French (3)
  public List<String> FrenchListStrProducer() { (4)
     List<String> res = Arrays.asList("bonjour");
     return res;
  }
}
1Producers should be declared in a Bean class
2It’s a field or a method annotated with @Produces (static methods supported)
3Producers can have qualifiers
4value of field or method returned value is the bean instance

Producers are an other kind of bean

they can have qualifiers like any other bean
producer method (or field) is called to create the bean instance
public class MyBean {

    @Inject
    @French
    private List<String> frenchListStrBean;

    @Inject
    public MyBean(List<Integer> initValues) {
       ...
    }
}

Disposers are Producer’s housekeeping

Disposers can free resources when produced bean is destroyed
public class ProducingBean {

    @Produces
    public MyNonCDIClass myProducer() {
      return new MyNonCdiClass();
    }

    public void releaseMyInstance(@Disposes MyNonCdiClass inst) { (1)
      inst.clean();
    }
}
1Will be called at the bean instance end of life
Notes

See them as @Predestroy for Producers

Producer methods can have parameters

Parameters will be injected by the container at instantiation time
They should have matching beans otherwise deployment fails
public class ProducingBean {

  @Produces
  public MyNonCDIClass listStrProducer(MyBean bean) { (1)
     ...
  }
}
1The container resolves MyBean and passes it to the producer when an instance of the bean is requested

Accessing Injection Point metadata

The container can provide metadata about the Injection Point
public class ProducingBean {

  @Produces
  public Logger produceLog(InjectionPoint injectionPoint) { (1)
     return Logger.getLogger(injectionPoint.getMember()
                  .getDeclaringClass().getName());
  }
}
1InjectionPoint is a SPI interface. The container can inject it to give info about the injection point
Notes

InjectionPoint can also be injected in managed bean

Typesafe Resolution

Putting the right a bean at the right place

Bean Types

A bean has a set of types depending on its definition
Bean types set for a managed bean contains the bean class, every superclass and all interfaces it implements directly or indirectly.
Bean types can be restricted by using @Typed annotation (Object is always in the set)

Bean types Example

public interface HelloService extends Hello { ... }

public class MyBean { (1)
    @Produces
    public HelloService prodHelloService() { ... } (2)
}



public class FrHelloService extends GenericService<String> implements HelloService { ... } (3)



@Typed({HelloService.class})

public class FrHelloService extends GenericService<String> implements HelloService { ... } (4)
1class MyBean and Object
2HelloService, Hello and Object
3FrHelloService, GenericService<String>, HelloService, Hello and Object
4HelloService and Object

Parameterized types count

Parameterized types information is kept (i.e. no type erasure)
public class MyBean {

    @Inject
    Service<User> userService;

    @Inject
    Service<Staff> staffService;    
}
Injection points above can be satisfied without ambiguity

Performing Typesafe Resolution

When resolving a bean to be injected to an injection point…​
…​the container considers bean type and qualifiers.
If the bean has a bean type that matches injection point type…​
…​and the bean has all the qualifiers of the injection point…​
…​the bean is assignable to the injection point.

Programmatic Lookup

Resolving beans at runtime

Meet Instance interface

Instance interface lets perform typesafe resolution at runtime
public class MyBean {

    @Inject
    Instance<HelloService> service; (1)

    public void displayHello() {
        display(service.get().hello()); (2)
    }
}
1Instance<T> injection points are always satisfied and never fail at deployment time
2with Instance<T> you control when bean a instance is requested with the get() method

Check bean existence at runtime

Instance<T> contains methods to test bean resolution
public class MyBean {

    @Inject
    Instance<HelloService> service;

    public void displayHello() {
        if (!(service.isUnsatisfied() || service.isAmbiguous())) { (1)
            display(service.get().hello());
        }
    }
}
1if tou skip the test get() may throw an exception

Looping on all beans in the Instance

Instance<T> is iterable
It’s where we use the @Any qualifier present on all beans
public class MyBean {

    @Inject
    @Any
    Instance<HelloService> services;

    public void displayHello() {
        for (HelloService service : services) {
            display(service.hello());
        }
    }
}

Select a bean by its qualifier

AnnotationLiteral is a helper class to create an annotation instance
public class MyBean {

    @Inject
    @Any
    Instance<HelloService> services;

    public void displayHello() {
            display(
                services.select(new AnnotationLiteral()<French> {}).get()); (1)
        }
}
1select() also accepts a type.
you can use TypeLiteral to create instances of parameterized types

Contexts

Beans life and death

Bean, Scope and Contexts

All Beans have a scope defined by an annotation
Each scope is associated to a context object
So each bean is bound to a context
The context is in charge of creating bean instances
The Container is in charge of creating and destroying contexts

Available scopes

CDI provides the following built-in scopes (and associated contexts):
  • @Dependent (default) bean has the same scope than its injector
  • @ApplicationScoped instance is linked to application lifecycle
  • @SessionScoped instance is linked to http session lifecycle
  • @RequestScoped instance is liked to http request lifecycle
  • @ConversationScoped lifecycle manually controlled within session
Instance is created at first request and destroyed with its bound context
CDI SPI allows third party to create their custom contexts

Examples

public class BaseHelloService implements HelloService { ... } (1)

@RequestScoped (2)
public class RequestService {
    @Inject HelloService service;
}

@ApplicationScoped  (3)
public class ApplicationService {
    @Inject RequestService service; (4)
}
1Bean has default scope @Dependent, instances are created for each injection
2Bean is @RequestScoped. Instance is created by request context and destroyed with request context
3Bean is @ApplicationScoped. Instance is created by application context and will live during all application
4No problem to inject bean from an other scope: CDI will provide the right bean

Good to know

instances are created when first accessed not with their context
A bean instance is a singleton in its context
With SPI you can manually destroy an instance in a context
A context can be inactive without being destroyed
Request context is more than a mapping to ServletRequest lifecycle

Events

More decoupling with

Events in action

public class FirstBean {
    @Inject Event<Post> postEvent; (1)

    public void saveNewPost(Post myPost) {
        postEvent.fire(myPost); (2)
    }
}

public class SecondBean {
    public void listenPost(@Observes Post post) { (3)
        System.out.println("Received : " + evt.message());
    }
}
1Event<T> is injected at the firing side. T is the event payload type (here my Post class)
2When needed, we fire the event with our instance of T (here the Post object to save)
3At the consuming side, we define an observer for a given payload type with @Observes annotation. If observed type match fired event type, the observer is called.

Observer resolution mimics typesafe resolution (in a looser way)

public class FirstBean {
    @Inject Event<Post> postEvent;

    public void saveNewPost(Post myPost) {
        postEvent.select(new AnnotationLiteral()<French> {}).fire(myPost);
    }
}

public class SecondBean {
    public void listenFrPost(@Observes @French Post post) {} (1)
    public void listenPost(@Observes Post post) {} (1)
    public void listenObject(@Observes Object obj) {} (1)
    public void listenEnPost(@Observes @English Post post) {} (2)
}
1These observers will be triggered (empty subset is accepted for qualifiers when resolving observers)
2This observer won’t be triggered

Hooking on context lifecycle with events

public class SecondBean {
    public void beginRequest(@Observes @Initialized(RequestScoped.class) ServletRequest req) {}

    public void endRequest(@Observes @Destroyed(RequestScoped.class) ServletRequest req) {}

    public void beginSession(@Observes @Initialized(SessionScoped.class) HttpSession session) {}

    public void endSession(@Observes @Destroyed(SessionScoped.class) HttpSession session) {}

    public void beginApplication(@Observes @Initialized(ApplicationScoped.class) ServlerContext ctx) {}

    public void endApplication(@Observes @Destroyed(ApplicationScoped.class) ServlerContext ctx) {}
}
Observer must be defined in a bean
Observer resolution may trigger bean instance creation.
So observers above are a nice way to initailize bean with its context

Interceptors and Decorators

Interceptors vs Decorators

They are both Aspect Oriented Programming tools
Interceptors are technical oriented: transaction, security, logging
Interceptors are bound to any bean or bean method
Decorators are business oriented: change the behaviour of a bean
Decorators are bound to a bean type

Interceptors

Interceptors are defined in their own specification
User should create an annotation as an interceptor binding
Interceptor class must be bound to an interceptor binding
Interceptor class contains intercepting methods
which are bound to lifecycle phase of intercepted bean
Interceptor binding is added to intercepted bean or method

Interceptor binding example

An interceptor binding is annotation used in two different places
On the interceptor and on the intercepted element to link them
@InterceptorBinding (1)
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Loggable {}
1This annotation comes from interceptor specification. It makes @Loggable an interceptor binding

Interceptor example

@Interceptor (1)
@Loggable (2)
@Priority(Interceptor.Priority.APPLICATION) (3)
public class LogInterceptor {
  @AroundInvoke (4)
  public Object log(InvocationContext ic) throws Exception {
      System.out.println("Entering " + ic.getMethod().getName());
      try {
          return ic.proceed();
      } finally {
          System.out.println("Exiting " + ic.getMethod().getName());
      }
  }
}
1We make the class an interceptor (@Interceptor comes from Interceptor spec)
2We bind this interceptor to the @Loggable interceptor binding
3We activate and give a priority to the interceptor (can be done in beans.xml config file as well)
4This intercepting method will be invoked instead of the intercepted one.

Interceptor Usage

@Loggable (1)
public class MyBean { ... }

public class MyOtherBean {

    @Loggable (2)
    public String hello() { ... }

}
1All bean’s method will be intercepted
2This method will be intercepted

Decorators

To be decorated, a bean should implement an interface
The decorator has to implement the same interface
It is declared with @Decorator annotation
It can be an abstract class (lets you choose methods to decorate)
It injects the decorated bean with @Delegate annotation

Decorator Example

@Decorator (1)
@Priority(Interceptor.Priority.APPLICATION) (2)
public abstract class HelloDecorator implements HelloService { (3)

    @Inject @Delegate HelloService service; (4)

    public String hello() { (5)
        return service.hello() + "-decorated";
    }
}
1Declares that the class is a decorator
2Activates and gives a priority to the decorator (could be done via beans.xml file)
3Decorators can be abstract and should share the same interface than decorated beans
4Decorated bean is annotated with @Delegate (other beans could be injected in decorator)
5Decorating method is called instead of decorated bean method. Delegate can be used in it.

Conclusion

Features not covered in this talk

Stereotypes creating annotation gathering multiple annotations
Alternatives replace a bean for tests or specific environments
Specialization bean overriding
Transactional events binding events to specific transaction phase
Check the spec on cdi-spec.org

References

Slides are accessible here j.mp/introcdi
Slides source github.com/antoinesd/Introduction-to-CDI
Slides generated with Asciidoctor and DZSlides backend
Original slide template - Dan Allen & _Sarah White
License of this doc: CC BY-SA 4.0

Antoine Sabot-Durand