Java EE CDI Producer methods tutorial

27 April 2013
By Gonçalo Marques
In this tutorial you will learn how to use CDI Producer methods in order to provide a flexible bean initialization mechanism. Concepts like polymorphism, disambiguation and CDI scopes will also be discussed as we proceed in the tutorial.

Introduction

Java EE CDI introduced a concept called Producer. Producers may be used to create - or produce - bean instances to be consumed by an application. Producers are also able to provide specific interface implementations according to the consumer needs so they are a valid way to support polymorphism in a CDI application.

Producers are also useful to encapsulate bean initialization or even to enable injection of object instances that are not themselves CDI managed beans into some points of our application.

This tutorial considers the following environment:

  1. Ubuntu 12.04
  2. JDK 1.7.0.21
  3. Weld 1.1.10
  4. Tomcat 7.0.35

Weld is the CDI reference implementation

The service

In this tutorial we will implement a Producer that will create and provide an example message sending service to its consumers. At the very beginning we will assume a single service implementation that represents message sending through email. Later we will define a second service implementation that will represent message sending through SMS.

Let's start by defining the service interface and a simple implementation:

Message sending service interface

package com.byteslounge.bean;

public interface MessageSender {

  void sendMessage();
  
}



Message sending service implementation

package com.byteslounge.bean.impl;

import com.byteslounge.bean.MessageSender;

public class EmailMessageSender implements MessageSender {

  @Override
  public void sendMessage() {
    System.out.println("Sending email message");
  }

}

So we defined the service interface and one implementation. In this scenario we defined the implementation that sends the message as an email message.

The producer

Now we define a CDI managed bean containing a Producer method that will be used to create MessageSender instances:

MessageSenderFactory.java

package com.byteslounge.bean;

import java.io.Serializable;

import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;

import com.byteslounge.bean.impl.EmailMessageSender;

@SessionScoped
public class MessageSenderFactory implements Serializable {

  private static final long serialVersionUID = 5269302440619391616L;

  @Produces
  public MessageSender getMessageSender(){
    return new EmailMessageSender();
  }
  
}

The method getMessageSender() is annotated with @Produces so the CDI container will know it produces MessageSender implementations. Now we may inject MessageSender implementations into our application:

Injecting a MessageSender implementation

@Inject
private MessageSender messageSender;

Multiple bean implementations (Polymorphism)

Let's define a second MessageSender implementation:

Message sending service implementation

package com.byteslounge.bean.impl;

import com.byteslounge.bean.MessageSender;

public class SmsMessageSender implements MessageSender {

  @Override
  public void sendMessage() {
    System.out.println("Sending SMS message");
  }

}

We may change the Producer method in order to support the Polymorphism:

Producer supporting polymorphism

package com.byteslounge.bean;

import java.io.Serializable;

import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;

import com.byteslounge.bean.impl.EmailMessageSender;
import com.byteslounge.bean.impl.SmsMessageSender;

@SessionScoped
public class MessageSenderFactory implements Serializable {

  private static final long serialVersionUID = 5269302440619391616L;

  private MessageTransportType messageTransportType;

  @Produces
  public MessageSender getMessageSender() {

    switch (messageTransportType) {
    
      case EMAIL:
        return new EmailMessageSender();
  
      case SMS:
      default:
        return new SmsMessageSender();

    }
  }
}

Polymorphism through qualifiers

We may also use qualifiers in order to support polymorphism. Let's see the MessageTransportType enum we used in the previous section:

MessageTransportType.java

package com.byteslounge.bean;

public enum MessageTransportType {
  EMAIL, SMS;
}

Now we define a new CDI qualifier as a Java annotation:

MessageTransport.java

package com.byteslounge.bean;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

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

  MessageTransportType value();
	
}

The qualifier has a property of type MessageTransportType that will be used to disambiguate between service implementations.

Finally we change the Producer in order to use the qualifier:

Polymorphism through qualifiers

package com.byteslounge.bean;

import java.io.Serializable;

import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;

import com.byteslounge.bean.impl.EmailMessageSender;
import com.byteslounge.bean.impl.SmsMessageSender;

@SessionScoped
public class MessageSenderFactory implements Serializable {

  private static final long serialVersionUID = 5269302440619391616L;

  @Produces
  @MessageTransport(MessageTransportType.EMAIL)
  public MessageSender getEmailMessageSender(){
    return new EmailMessageSender();
  }
  
  @Produces
  @MessageTransport(MessageTransportType.SMS)
  public MessageSender getSmsMessageSender(){
    return new SmsMessageSender();
  }
  
}

Each Producer method will return a distinct message service implementation depending on the qualifier used in the injection point.

Now when we inject the message sending service we may choose which implementation to use by the means of the CDI qualifier:


// Inject the email message sending service
@Inject
@MessageTransport(MessageTransportType.EMAIL)
private MessageSender messageSender;




// Inject the SMS message sending service
@Inject
@MessageTransport(MessageTransportType.SMS)
private MessageSender messageSender;

Producing CDI managed beans

What if we need to Produce CDI managed beans, ie. beans which initialization process should be handled by CDI itself? For example, producing a bean that also has its own CDI dependencies that should be processed by the container during initialization.

We may use injection parameters in the producer method:


@Produces
public MessageSender getEmailMessageSender(
  EmailMessageSender emailMessageSender){
  
  return emailMessageSender;
}

When we inject a MessageSender implementation in our application the CDI container will initialize a EmailMessageSender instance and inject it into the Producer method as a parameter. Then the method will return the new service instance initialized by CDI.

Producer method scopes

Producer methods should be seen as independent beans, ie. they also have their own scope. They do not inherit the scope of the bean where they are defined!

By default a Producer method has Dependent scope. This means that every time we fetch an instance produced by some Producer method, the method will always be called. What happens if we change the Producer method scope?

@Produces
@SessionScoped
public MessageSender getEmailMessageSender(
  EmailMessageSender emailMessageSender){
  
  return emailMessageSender;
}

Now we defined our Producer method as being SessionScoped. This means that only one method execution will ever be executed per HTTP session. The returned instance - EmailMessageSender - will also be SessionScoped.

What if we are injecting some bean into the Producer that has a distinct scope from the Producer itself?

@Produces
@SessionScoped
public SomeService getService(SomeService someService){
  return someService;
}

If SomeService parameter is a bean defined - for example - as RequestScoped it would be promoted to SessionScoped by the Producer method and then returned to the caller. This is a problem!

The SomeService instance will be destroyed as soon as the current HTTP request finishes and CDI will then have a broken reference to SomeService - supposedly SessionScoped - instance and consequently provide unpredictable results to further SomeService injections.

We could change the scope of SomeService bean but that would affect all other consumers of the bean! Another approach is to use the New qualifier:

@Produces
@SessionScoped
public SomeService getService(@New SomeService someService){
  return someService;
}

With the New qualifier CDI will always inject a new Dependent instance into the Producer method that will then be safely promoted to the Producer scope - in this case session scope - which in turn will return it to the caller.

Disposing

There is also the case where we may need to clean up the produced resources. Further information on resource disposing is available in the following tutorial : Java EE CDI Disposer methods example.

At the end of this page is a downloadable sample that runs on Tomcat and shows Producer polymorphism support through qualifiers. The example consists in the injection of the message sending service in a servlet.

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: