ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Using Dependency Injection in Java EE 5.0

by Debu Panda
01/04/2006

Dependency injection, or inversion of control (IOC), is today's latest development craze. IOC containers such as Spring have become popular because they simplify the complexities of enterprise Java that come mostly from Java Naming and Directory Interface (JNDI). In this article, I'll discuss how you can use dependency injection in the upcoming Java EE 5.0 specification for resources and services. I migrated the J2EE 1.4 Blueprint application Java Adventure Builder to use EJB 3.0, web services metadata, and dependency injection; I'll use this application to illustrate concepts in this article.

What is Dependency Injection?

Most enterprise Java applications use external resources and services such as DataSources, EJBs, or web services. In J2EE 1.4, the client must explicitly declare the dependency on the resources in the deployment descriptor element and obtain a reference to the resource by doing a JNDI lookup.

For example, if you want to use a resource such as a DataSource or a service such as EJB in J2EE 1.4, you must define it in the deployment descriptor like this:

<ejb-local-ref>
        <ejb-ref-name>ejb/HelloWorld</ejb-ref-name>
        <local>oracle.ejb30.HelloWorld</local>
 </ejb-local-ref>

Then, you'd have to look up the object using JNDI, as follows, before you could use the resource:

Context ic = new InitialContext();
HelloWorld helloWorld = (
    HelloWorld)ic.lookup("java:comp/env/ejb/HelloWorld");

This method is not only hard for new Java developers to understand, but also error-prone, and thus, it's blamed for some of the complexities in J2EE 1.4.

Dependency injection is the inverse of JNDI. It lets you declare dependencies and lets the Java EE container handle the complexities of service or resource instantiation and initialization when the resource is required. Based on the declaration of resources using annotations or deployment descriptors, the Java EE 5.0 container injects an instance of the resource when it's required. Figure 1 compares JNDI with dependency injection:

Comparing JNDI and dependency injection
Figure 1. Comparing JNDI and dependency injection

Where Can You Use Dependency Injection?

Dependency injection can only be used by managed classes--those that are managed by Java EE containers such as EJB or servlets--rather than by all classes such as helper classes. For example, if we have an EJB, we can use dependency injection on an EJB 3.0 bean class, but not on a helper class upon which the EJB depends. Java EE 5.0 defines dependency injection of resources and EJB, web services in EJB, web and application client modules. The following table lists the types of classes in web and EJB modules that support dependency injection.

Container Type of managed classes Resource type

Web

Servlet, listener classes, web services end-point, JAX-RPC handlers

DataSource, JMS, Mail, EJB, Environment entries, EntityManager, UserTransaction

EJB

Bean, interceptors, web services end-point

DataSource, JMS, Mail, Environment entries, EntityManager, EJB Context, UserTransaction, TimerService

Some Java EE containers, such as Oracle Application Server 10g 10.1.3 and JBoss Application Server 4.0, provide early support for EJB 3.0, so they support dependency injection in the EJB container.

J2EE Design Patterns

Related Reading

J2EE Design Patterns
By William Crawford, Jonathan Kaplan

Using Dependency Injection in Java EE

As I outlined earlier, you can either use metadata annotations or deployment descriptor elements to declare dependencies upon resources. The Common Metadata annotations for Java Platform under JSR 250 that was recently proposed as a final draft defines two annotations (javax.annotation.Resource and javax.annotation.Resources) for defining dependencies on resources. EJB 3.0 under JSR 220 defines javax.ejb.EJB for injection of EJBs, while the Java API for XML web services 2.0 defines the javax.xml.ws.WebServiceRef annotation for injecting web services references.

The Resource annotation can be used on a class or methods or fields of a managed class, such as an EJB or servlet. You can use the Resource annotation to define dependencies on any type of resource, such as DataSource, JMS, Mail, URL, or environment entries.

Here's the definition of the javax.annotation.Resource interface:

public @interface Resource {    
public enum AuthenticationType {CONTAINER,APPLICATION
    }    
String name() default '';    
Class type() default Object.class;    
AuthenticationType authenticationType() 
            default  AuthenticationType.CONTAINER;    
boolean shareable() default true;
String mappedName default ''; 
description() default '';}

The following table defines the parameters that can be specified with javax.annotation.Resource:

Parameter

Type

Description

Default

Name

String

The JNDI name of the resource being used. If the type isn't specified, it's derived from the name of the field or property being injected.

""

Type

Class

The type of resource being used. If the type isn't specified, it's derived from the type of field or property being injected.

Object.class

authenticationType

Enum AuthenticationType { CONTAINER, APPLICATION }

The type of authentication needed to use the resource.

CONTAINER

shareable

Boolean

Indicates the resource is shareable.

false

description

String

Brief description of this resource.

""

mappedName

String

A product-specific name to which the resources should be mapped. The mappedName is vendor-dependent; you cannot port it across containers.

""

Types of Injection

Java EE 5.0 supports two types of injection: field and setter. Field injection lets you inject a resource to a field, while setter injection lets you inject a resource by invoking a setter method.

To use a field injection, simply define a field and annotate it to be a resource reference. If you don't define the resource's name and type, the container will derive this information from the field's name and type. For example, you can inject a DataSource to a field as follows:

@Resource 
 private javax.sql.DataSource AdventureDB;

In this example, a DataSource with JNDI name AdventureDB should be configured; if not, the container will throw an exception. The injection must be completed before any methods of the managed class can accessed by a client.

Conversely, setter injection (or "property injection") lets you define a set...() method using JavaBeans rules, and annotate it as a resource reference. The application doesn't need to invoke the setter method; the Java EE container method will invoke the setter method before it invokes the business methods to which the resources are being injected. For example, you must have a setter method named setAdventureDB if you have a field named adventureDB. If you don't define the name or type of a setter injection, it will be derived from the setter method's name and parameter type. For example, here is a setter method with a resource reference:

@Resource 
private void setAdventureDB(javax.sql.DataSource ds) 
{ 
 adventureDB = ds; 
} 
private DataSource adventureDB;

Using Resources with Dependency Injection

Let's look at some typical ways in which dependency injection is used in Java EE applications with the Resource annotation.

Using DataSource

Data sources are often used in enterprise Java applications. You can use dependency injection to obtain a DataSource instead of using JNDI. For example, if you have a DataSource named jdbc/AdventureDB in your environment, you can obtain a reference to that data source as follows:

 @Resource(name="jdbc/AdventureDB") 
 private javax.jdbc.DataSource myDB; 
 Connection con;
 con = myDb.getConnection();

Using JMS Resources

The Java Message Service (JMS) has made Message-Oriented Middleware (MOM) very easy to use and has led to the widespread use of both technologies in Java EE applications. You can use resource annotation to inject JMS destinations such as Queue or Topic, or resource factories such as connection factories. For example, if you want to use a JMS resource, you'd first create it in the server configuration and then define the dependency using a @Resource annotation, like this:

 @Resource(name="jms/WorkFlowManagerQueue")
 private Queue wfmQueue;

Using Environment Entries

Environment entries let you specify business parameters that may vary from time to time, or from one environment to another. For example, suppose that you want to set the maximum number of trades that a user can make per month in your application. It doesn't make sense to hard-code this into your application, because you might want to change the value in the future, or use different values for test and production systems.

If you want the value for maxTradesAllowedPerUser to be 50, you can define it as follows:

@Resource int maxTradesAllowedPerUser = 50

However, it doesn't make sense to use annotation to define environment entries, because the annotation is part of the application code. Therefore, you'd use the deployment descriptor to inject the value:

 <env-entry> 
 <env-entry-name>maxTradesAllowedPerUser</env-entry-name> 
 <env-entry-type>java.lang.Integer</env-entry-type> 
 <env-entry-value>15</env-entry-value> 
 <injection-target> 
 <injection-target-name>maxTradesAllowedPerUser</injection-target-name> 
 </injection-target> 
 </env-entry>

Using a Mail Resource

You can use the Resource annotation to inject an instance of a mail session.

First, you'd configure a mail resource in your application server. Then you can inject the mail session into your application using a Resource annotation:

 @Resource(name="mail/Adventure ") 
 private javax.mail.Session ms;

EJBContext

You can use the Resource annotation to use inject SessionContext and MessageDrivenContext, like this:

@Resource javax.ejb.SessionContext ctx

TimerService

TimerService gives EJB components access to the container-managed timer service, and lets you schedule tasks and activities from EJB applications. You can inject TimerService into an EJB using resource annotation with the following code:

 @Resource javax.ejb.TimerService ts;

Using Multiple Resources with javax.annotation.Resources

If you want to use multiple resources in your application, use javax.annotation.Resources as follows:

 @Resources ({     
 @Resource(name="jdbc/AdventureDB" type=javax.sql.DataSource), 
 @Resource(name="jms/wfmQCF" type=javax.jms.QueueConnectionFactory)})

Using EJBs with Dependency Injection

In J2EE 1.4, EJB is very complex and it inherits some of the complexities of JNDI. In EJB 3.0, the dependency on another session bean is expressed using the javax.ejb.EJB annotation or the ejb-ref element deployment descriptor element.

Here's the definition of the javax.ejb.EJB annotation:

 @Target({TYPE, METHOD, FIELD}) 
 @Retention(RUNTIME) 
 public @interface EJB { 
 String name() default ""; 
 String beanName() default ""; 
 String mappedName() default ""; 
 String description() default ""; 
 Class beanInterface() default Object.class; 
 }

The following table shows the parameters for javax.annotation.EJB :

Parameter

Type

Description

Default

name

String

The JNDI name of the EJB that's being used. If it's not specified, it's derived from the name of the field or property that's being injected.

""

beanName

String

The name of the EJB, defined either by using the ejb-name element or the name parameter of the bean class.

""

beanInterface

Class

The name of the interface implemented by the object returned by lookup.

mappedName

String

A product-specific name to which the service should be mapped.

""

description

String

Brief description of the EJB.

""

If you want to use an EJB named ProcessManager from a servlet or another EJB, you can use dependency injection to get an instance of the ProcessManager EJB and invoke a method:

@EJB(name="ProcessManager") 
 private ProcessManager pm;
pm.submitOrder(order);

Here, the name is the JNDI name of the EJB being injected. The interface exists for cases when an EJB is used on a class and there is no way of knowing which interface is being used by the component. The beanName is used to disambiguate a bean from another if they both implement the same interface.

Using Web Services with Dependency Injection

Developing and invoking web services is very complex in J2EE. Java EE 5.0 simplifies the development and invocation of XML-based web services by using web services metadata (JSR-181) and the Java API for XML web services 2.0 (JSR-224). You can use the javax.xml.ws.WebServiceRef annotation to define dependency and injection targets for a web service.

The following table describes the parameters that can be passed to the WebServiceRef annotation:

Parameter

Type

Description

Default

name

String

The JNDI name of the service being used. If it's not specified, then it's derived from the name of the field or property into which it's being injected.

""

type

Class

The type of resource being used. If the type is not specified, then it's derived from the type of field or property into which it's being injected.

Object.class

Value

Class

The service class type must extend the javax.xml.ws.Service class.

Object.class

WsdlLocation

String

The URL that points to the location of the WSDL of the web service that's being referred to.

""

mappedName

String

A product-specific name to which the service should be mapped.

""

Here's an example of a WebService annotation. The values for type and name are derived from the injection target:

 @WebServiceRef 
 public PurchaseOrderService poService;

The Benefits and Limitations of Dependency Injection

Benefits

Dependency injection makes resources and services easier to use because you don't have to deal with the complexities of JNDI. You don't have to write hundreds of lines of code or write service locator patterns.

Java EE supports dependency injection either via XML or annotation, so use annotation only when it makes sense. Annotation can make your code more readable and concise--not to mention make your life easier--but it can also cause maintenance problems, since the method requires that resource references are hard-wired into the application code. The good news is that you can override annotations using XML elements.

Limitations

Because Java EE 5.0 supports dependency injection with managed classes, you can't use dependency injection from helper classes. And you must still use JNDI if you want to use resources or services from a helper class.

Conclusion

Dependency injection will greatly simplify the complexities of JNDI and make it easier to develop enterprise applications. See for yourself how it makes using resources and services easier: try it out in Oracle Application Server 10g 10.1.3 and JBoss 4.0.x, both of which offer early support for EJB 3.0 and dependency injection in the EJB container.

Resources

Debu Panda is a Senior Principal Product Manager of the Oracle Application Server development team.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.