CDI (Context and dependency injection) is a great specification introduced in the JEE6 that offers a lot of resources. One of them is the Producer methods, and we’ll be talking about it in this post.
CDI Producer can be used in some scenarios, such as:
- Making Non-CDI beans eligible to be injected into other CDI beans. You may be using some library that doesn’t expose their beans via CDI but you want to inject them in your CDI beans by convenience.
- You have a bean that requires a constructor with some argument.
These are some scenarios where CDI Producer methods can be applied.
You can also watch the video below, where I show step-by-step the creation of the basic example and a few more details:
Basic example
Let’s see through a basic example how to use CDI Producer methods. Imagine that you have an interface and one implementation for this interface, but this implementation only provides a constructor that takes one argument. In this case, you cannot inject this class automatically in other CDI beans, as it doesn’t provide a default constructor.
package com.lucianomolinari.cdiproducer.dao; public interface SimpleDao { void doSomething(); }
package com.lucianomolinari.cdiproducer.dao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileSimpleDao implements SimpleDao { private final Logger logger = LoggerFactory.getLogger(getClass()); private final String baseDir; public FileSimpleDao(String baseDir) { this.baseDir = baseDir; } public void doSomething() { logger.info("Invoking doSomething() on FileSimpleDao with baseDir {}", baseDir); } }
As you can see, we have a basic interface and 1 implementation for it. In our client code, we want to be able to use this implementation as we do for any CDI bean.
package com.lucianomolinari.cdiproducer.client; import java.io.IOException; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.lucianomolinari.cdiproducer.dao.SimpleDao; @WebServlet(urlPatterns = "/client") public class ServletClient extends HttpServlet { private static final long serialVersionUID = 1316100980591892471L; @Inject private SimpleDao simpleDao; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { simpleDao.doSomething(); } }
If we run this code by accessing this servlet at http://localhost:8080/cdiproducer-0.0.1-SNAPSHOT/client, we will get the following error:
WELD-001408: Unsatisfied dependencies for type SimpleDao with qualifiers @Default at injection point [BackedAnnotatedField] @Inject private com.lucianomolinari.cdiproducer.client.ServletClient1.simpleDao at com.lucianomolinari.cdiproducer.client.ServletClient1.simpleDao(ServletClient1.java:0)
That happens because there’s no implementation of the SimpleDao interface that is a CDI bean. In order to solve this issue, we can create a CDI producer method, responsible for creating an instance of FileSimpleDao that allows it to be injected in any CDI bean.
package com.lucianomolinari.cdiproducer.producer; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.lucianomolinari.cdiproducer.dao.FileSimpleDao; import com.lucianomolinari.cdiproducer.dao.SimpleDao; public class SimpleDaoProducer { private final Logger logger = LoggerFactory.getLogger(getClass()); @Produces @ApplicationScoped public SimpleDao createFileDao() { logger.info("--> Creating fileDao"); return new FileSimpleDao("/opt/"); } }
Now, when injecting an instance of SimpleDao, CDI will look for a valid CDI bean or for some producer method that is creating an instance of that type.
If we run the code again now, the following output will be got:
22:31:02,892 INFO [com.lucianomolinari.cdiproducer.producer.SimpleDaoProducer] (default task-1) --> Creating fileDao 22:31:02,893 INFO [com.lucianomolinari.cdiproducer.dao.FileSimpleDao] (default task-1) Invoking doSomething() on FileSimpleDao with baseDir /opt/
As you can see, now everything is working fine.
CDI Producer and scopes
You may be wondering when the instances of SimpleDao are created: one per application, per request..
Actually, you can use any CDI scope in your producer. It’s important to notice, however, that you should place the scope annotation in the producer method and not in the producer class. For example, if you want to make sure that only one instance of the bean is created, you need to annotate the producer method with @ApplicationScoped instead of using this annotation in the producer class.
So, that was a quick introduction to CDI producer and you can find the complete code here. You can also watch the video above, where I show everything working.