Using Dependency Injection with Java EE
by samdhukate11@gmail.com
by samdhukate11@gmail.com
When Java EE 6 introduced the Contexts and Dependency Injection (CDI) specification, I was amazed that Java EE finally included dependency injection (DI) horizontally. In other words, any component can be injected into any other component, regardless of whether it's an EJB, a web service, or a POJO. Thus, you can inject a component from a Java EE container using the @Inject annotation, since the file named beans.xml is present in the WEB-INF/META-INF directory to indicate to the container which classes should be scanned.
Some questions appeared in my head after the new spec was released, and I considered the following:
Regarding the frameworks that provide DI: do I have to migrate to Java EE and forget they exist? No, you must think in CDI when you want to migrate a full profile of Java EE container.
Regarding the @EJB annotation: is it now deprecated? No, you can use the @EJB annotation normally, but this annotation isn't needed when you want to inject local components. In this case, I suggest to use @Inject and forget @EJB because CDI attends to both components.
Which annotation should be used with JSF: @Named or @ManagedBean? @Named, because some JSF annotations were created to provide similar features offered by the CDI spec, and now they should be discontinued on my vision.
Based on my above considerations, I created five examples that show how to use CDI with some Java EE specifications.
Example 1 - CDI and JAX-WS:
@WebService(portName = "HelpSoapPort", serviceName = "HelpSoapService",
name = "HelpWS", targetNamespace = "http://localhost:8080")
public class HelpSoap {
@Inject HelpCDIService helpCDIService;
@Inject HelpEJBService helpEJBService;
@WebMethod
public String ping() {
return helpEJBService.ping();
}
}
name = "HelpWS", targetNamespace = "http://localhost:8080")
public class HelpSoap {
@Inject HelpCDIService helpCDIService;
@Inject HelpEJBService helpEJBService;
@WebMethod
public String ping() {
return helpEJBService.ping();
}
}
In the example above, both components (CDI and EJB) can be injected using the @Inject annotation and applicable within a web service SOAP.
If you want to change the web service to be an EJB, you just have to include an @Stateless, @Stateful, or @Singleton annotation, and no more.
Example 2 - CDI and JAX-RS
@Path("/helps")
public class HelpsResource {
@Inject HelpCDIService helpCDIService;
@Inject HelpEJBService helpEJBService;
@GET
@Path("/ping")
public String ping() {
return helpEJBService.ping();
}
}
public class HelpsResource {
@Inject HelpCDIService helpCDIService;
@Inject HelpEJBService helpEJBService;
@GET
@Path("/ping")
public String ping() {
return helpEJBService.ping();
}
}
For the JAX-RS spec, you can do the same thing described previously.
Example 3 - CDI and EJB:
@Stateless
public class HelpEJBService {
@Inject AnotherHelpEJBService anotherHelpEJBService;
public String ping() {
return anotherHelpEJBService.ping();
}
}
public class HelpEJBService {
@Inject AnotherHelpEJBService anotherHelpEJBService;
public String ping() {
return anotherHelpEJBService.ping();
}
}
For the EJB spec, you can also use the @Inject annotation to inject another EJB.
Example 4 - CDI and JSF:
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
...
@Named
@RequestScoped
public class FooNamedController {
@Inject HelpEJBService helpEJBService;
public String displayWelcomeMessage() {
return helpEJBService.displayWelcomeMessage();
}
}
import javax.inject.Inject;
import javax.inject.Named;
...
@Named
@RequestScoped
public class FooNamedController {
@Inject HelpEJBService helpEJBService;
public String displayWelcomeMessage() {
return helpEJBService.displayWelcomeMessage();
}
}
For JSF I suggest using CDI both for exposing components to XHTML pages and for defining scopes. But if you want to use the @ManagedBean annotation with the view scope provided only by JSF, you continue to be able to use CDI, as shown below:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
...
@ManagedBean
@ViewScoped
public class FooManagedBeanController {
@Inject HelpEJBService helpEJBService;
public String displayWelcomeMessage() {
return helpEJBService.displayWelcomeMessage();
}
}
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
...
@ManagedBean
@ViewScoped
public class FooManagedBeanController {
@Inject HelpEJBService helpEJBService;
public String displayWelcomeMessage() {
return helpEJBService.displayWelcomeMessage();
}
}
Example 5 - CDI and JNDI:
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.enterprise.inject.Produces;
import javax.inject.Qualifier;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
import javax.sql.DataSource;
import javax.xml.ws.WebServiceRef;
@Singleton
public class JavaEEContainerResourceAdaptor {
@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
public @interface DefaultDataSource {}
@Produces
@WebServiceRef(lookup = "java:app/foo-app/HelpSoap")
static HelpSoap helpSoap;
@Produces
@Resource(lookup = "java:jboss/datasources/ExampleDS")
@DefaultDataSource
static DataSource defaultDS;
@Produces
@PersistenceContext(unitName = "DefaultPU")
@DefaultDataSource
EntityManager defaultEntityManager;
@Produces
@PersistenceUnit(unitName = "DefaultPU")
@DefaultDataSource
EntityManagerFactory defaultEntityManagerFactory;
}
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.enterprise.inject.Produces;
import javax.inject.Qualifier;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
import javax.sql.DataSource;
import javax.xml.ws.WebServiceRef;
@Singleton
public class JavaEEContainerResourceAdaptor {
@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
public @interface DefaultDataSource {}
@Produces
@WebServiceRef(lookup = "java:app/foo-app/HelpSoap")
static HelpSoap helpSoap;
@Produces
@Resource(lookup = "java:jboss/datasources/ExampleDS")
@DefaultDataSource
static DataSource defaultDS;
@Produces
@PersistenceContext(unitName = "DefaultPU")
@DefaultDataSource
EntityManager defaultEntityManager;
@Produces
@PersistenceUnit(unitName = "DefaultPU")
@DefaultDataSource
EntityManagerFactory defaultEntityManagerFactory;
}
The code above shows how to provide JNDI resources to be injected using CDI. You need to create an adaptor to expose the resources as producer fields. You can also expose resources as producer methods, but the simpler way is using the @Resource annotation.
Below you'll see how to inject these resources using the @Inject annotation and the @DefaultDataSource qualifier from a RESTful web service:
@Path("/helps")
public class HelpsResource {
private static final int VALIDATION_TIMEOUT_IN_SECONDS = 30;
@Inject
@DefaultDataSource
DataSource dataSource;
@GET
@Path("/ping/db")
public String pingDB() {
Boolean connected = false;
try {
connected = dataSource.getConnection().isValid(
VALIDATION_TIMEOUT_IN_SECONDS);
} catch (SQLException e) {
connected = false;
}
return connected ? "Pong!" : "Sorry :(";
}
}
Conclusion
CDI is quite powerful and will continue being the dependency injection mechanism of Java EE for a long time. I also believe that gradually specifications that provide some kind of DI like EJB will be replaced by features offered by the CDI.
public class HelpsResource {
private static final int VALIDATION_TIMEOUT_IN_SECONDS = 30;
@Inject
@DefaultDataSource
DataSource dataSource;
@GET
@Path("/ping/db")
public String pingDB() {
Boolean connected = false;
try {
connected = dataSource.getConnection().isValid(
VALIDATION_TIMEOUT_IN_SECONDS);
} catch (SQLException e) {
connected = false;
}
return connected ? "Pong!" : "Sorry :(";
}
}
Conclusion
CDI is quite powerful and will continue being the dependency injection mechanism of Java EE for a long time. I also believe that gradually specifications that provide some kind of DI like EJB will be replaced by features offered by the CDI.
Using Dependency Injection with Java EE
No comments:
Post a Comment