Java EE CDI - Inject beans from external (3rd party) libraries example
Introduction
When we need an external JAR to be scanned by a Java EE container for CDI beans we must define the JAR as a target for CDI bean scanning, ie. via beans.xml file. What if the JAR is provided by an external - or 3d party - entity?
It may not be suitable at all to modify the 3rd party JAR in what matters to library maintenance for future versions compatibility. In this tutorial we will see how we can overcome this restriction and inject beans from 3d party libraries.
This tutorial considers the following environment:
- JDK 1.7.0.21
- Weld 1.1.10
Weld is the CDI reference implementation
Injecting a simple bean from a 3rd party library
We start this tutorial by showing how we may inject a SimpleEmail instance from Apache Commons Email library. Usually one creates a SimpleEmail instance like the following:
SimpleEmail simpleEmail = new SimpleEmail();
What if we want to be able to inject SimpleEmail bean instances by using the @Inject annotation? We may write a producer method to achieve this goal:
package com.byteslounge.cdi.producer; import javax.enterprise.inject.Produces; import org.apache.commons.mail.SimpleEmail; public class CommonsEmailProducer { @Produces public SimpleEmail simpleEmailProducer(){ return new SimpleEmail(); } }
Now we may inject the SimpleEmail bean in a CDI fashion:
@Inject private SimpleEmail simpleEmail;
Passing initialization parameters
We may also need to pass initialization parameters to external library beans. Supposing we want to enable injection of FTPClient beans from Apache Commons Net library. We may give the ability for bean clients to specify the FTP server hostname, obtaining an already connected FTPClient instance.
We start to specify an annotation to be used along with bean injection:
package com.byteslounge.cdi.producer; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Retention(RUNTIME) @Target(FIELD) public @interface CommonsFtpHostname { String value(); }
Now a CDI producer method that creates FTPClient instances taking the annotation into account:
package com.byteslounge.cdi.producer; import java.io.IOException; import java.net.SocketException; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.InjectionPoint; import org.apache.commons.net.ftp.FTPClient; public class CommonsNetProducer { @Produces public FTPClient ftpClientProducer(InjectionPoint ip) throws SocketException, IOException{ Annotated annotated = ip.getAnnotated(); FTPClient ftpClient = new FTPClient(); // Get annotation CommonsFtpHostname commonsFtpHostname = annotated.getAnnotation(CommonsFtpHostname.class); // If FTP server hostname was supplied we // provide an already connected FTP client if(commonsFtpHostname != null){ ftpClient.connect(commonsFtpHostname.value()); } return ftpClient; } }
Now we may inject the FTPClient bean while specifying the server hostname:
@Inject @CommonsFtpHostname(value = "ftp.domain.com") private FTPClient ftpClient;
A very common use case
There is a very common use case of 3rd party library bean injection that consists in injecting a Logger instance. The producer may look like the following:
package com.byteslounge.cdi.producer; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.InjectionPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggerProducer { @Produces public Logger loggerProducer(InjectionPoint ip){ return LoggerFactory.getLogger( ip.getMember().getDeclaringClass().getName()); } }
Now we may inject the Logger simply as:
@Inject private Logger logger;