2010-07-11

CDI + JSF 2.0. Part 1

In my previous post I complained about the standard JSF dependency injection mechanism. Therefore I decided it was the right time to look at...

CDI - Contexts and Dependency Injection (JSR 299)


It might be a good idea to read the JSR 299 document (the evaluation version is for you; the other one is for JSR implementers like Guice or Spring) if you haven’t yet.

CDI (formerly known as Web Beans) differs from DI in that it introduces the concept of Context. So you can inject dependencies according to the context too. What contexts are there?
  • Application
  • Request
  • Session
  • Conversation
  • custom

That allows you to have a very flexible dependency injection mechanism where a new object is created by the container and injected whenever it does not exist in your context (or in broader context).


No more @ManagedBean


That’s right. This is how your JSF managed bean will look like now:

package me.m1key.jsf;

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

// ...

@Named
@RequestScoped
public class DogsBean {   

In fact, we wouldn’t have to use the @Named annotation to make it a managed bean, because according to the specification (consult JSR) most Java classes are managed beans (i.e. you can use dependency injection in them or inject them). We need @Named, however, to reference that bean from JSF. You can specify another name (the default one is dogsBean in this case).


Accessing a managed bean from JSF


It’s simple.
<h:form>

<h:inputText value="#{dogsBean.name}" />

<h:commandButton value="Send" action="#{dogsBean.send}" />
</h:form>
And that’s it. Our bean is request scoped (CAUTION: make sure you import @RequestScoped from the correct package! That is javax.enterprise.context and not javax.faces.bean) so there will be a new instance of it per request.

Let’s take a look at...

Dependency injection


Let’s create a DAO.

package me.m1key.jsf.dao;

// ...

public interface DogsDao {

 public List<Dog> retrieveAllDogs();

}

package me.m1key.jsf.dao;

import javax.enterprise.context.ApplicationScoped;
// ...

@ApplicationScoped
public class DogsDaoImpl implements DogsDao {

 public DogsDaoImpl() {
     System.out.println(" DogsDaoImpl created: " + this);
 }

 public List<Dog> retrieveAllDogs() {
     List<Dog> dogs = new ArrayList<Dog>();

     Dog dog1 = new Dog();
     dog1.setId(1L);
     dog1.setName("Sega");
     dogs.add(dog1);

     Dog dog2 = new Dog();
     dog2.setId(2L);
     dog2.setName("Fluffy");
     dogs.add(dog2);

     return dogs;
 }

}

Great! This bean is Application scoped, so there is going to be one instance of it per application. Note: the constructor will be called twice, actually. One for the actual object, one for the proxy. Read more about this behavior or consult JSR 299, Section 5.4.

Now, let’s inject it.

 @Inject
 private DogsDao dogsDao;

That’s it. You ask for an interface injection. The container sees there is only one implementation, and injects it. But what if you want to have two implementations of DogsDao?

package me.m1key.jsf.dao;

import javax.enterprise.context.ApplicationScoped;

// ...

@ApplicationScoped    
public class DogsDaoMockImpl implements DogsDao {

With the current configuration, you’d get an exception - the container would not be able to decide which one to instantiate for you. How can you further specify which implementation you need? You should use...

Qualifiers


Qualifiers are a CDI concept that allows you to have more sophisticated criteria for selecting the implementation. Let’s create two qualifiers.

package me.m1key.jsf.qualifiers;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

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

package me.m1key.jsf.qualifiers;

// ...

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

Qualifiers must be annotated with @Qualifier.

Now, let’s annotate the classes.

// ...
import me.m1key.jsf.qualifiers.Actual;

@Actual
@ApplicationScoped
public class DogsDaoImpl implements DogsDao {

// ...
import me.m1key.jsf.qualifiers.Mock;

@Mock
@ApplicationScoped
public class DogsDaoMockImpl implements DogsDao {

Now, when we inject, we must specify which implementation we want, with the same qualifier.

@Inject
@Actual
private DogsDao dogsDao;

And now it works.


Summary


I explained how to use basic CDI which is a new feature of JEE6. Remember that this is a JSF example, so I assumed you run it on an application server. However, CDI itself can be run without a server, so long as you provide an implementation for it (like Weld; the situation is similar to JPA).

Download source code for this article

Next article


In the next article I will explain why and how you can use the @Produces annotation and how to use alternatives (alternatives in the CDI sense).

No comments:

Post a Comment