Archive for June 4th, 2005

Is Dependency Injection the Golden Hammer?

Saturday, June 4th, 2005

I have enjoyed reading Chris Richardson's blog and look forward to reading his new POJO in action book. In a recent blog entry, Chris identified some limitations of the EJB 3.0 Dependency Injection (DI) model. I agree with his assessment that we should be able to use EJB 3 annotation to inject service objects into any POJO (servlets etc.) -- not just EJBs. I hope we will be able to provide this functionality in JBoss's EJB 3 implementation soon.

Chris also mentioned that EJB 3 does not support injecting arbitrary POJOs beyond the JNDI. He proposed to get around this limitation by injecting Spring bean factories into the EJB 3.0 application. Well, I have to say that I do not entirely agree with him. In my view, IoC containers like Spring overuse DI as a Golden Hammer, and they often create unnecessary burden for the regular application developers. Further more, even if Spring-like DI functions are needed, there is often a better way than injecting the bean factory, especially in the JBoss context. Here is why.

To answer when and where DI is appropriate, we have to first understand why we need it in the first place. From an architecture point of view, DI is just an implementation approach and the goal is build "loosely coupled" applications. The idea behind "loose coupling" is to separate the interfaces from implementations. People have been building loosely coupled applications long before DI is invented. But those non-DI methods have some drawbacks:

  • Plain old factory classes
    • Lacks an application-wide infrastructure for creating and managing objects.
    • The dependencies are not managed.
    • Ad-hoc API -- not a "framework" that people can learn and reuse.
  • Dependency Lookup (DL) via JNDI in J2EE containers
    • The client and service must agree on the string-based name.
    • The retrieved object type is not checked and could result in casting error at runtime.
    • The verbose lookup code is repeated and littered across the application.
  • Container callback APIs such as the setSessionContext() in the servlet container.
    • Must inherit from "framework" classes (e.g., the Servlet class)-- not a POJO.
    • A non-POJO component object is difficult to test, as you have to mock the container.

The real value of DI is to address the above problems and make it easy and elegant to implement loosely coupled architecture. With this in mind, I think EJB 3.0 does a far superior job than most other "DI (IoC) containers" out there. Just compare the following two code snippets to inject a persistence manager object into the application. The first is the injection of EJB 3.0's EntityManager.

JAVA:
  1. public class FooDao {
  2.   @PersistenceContext protected EntityManager em;
  3.   // ... ...
  4. }

The second is the injection of Hibernate session factory in a Spring container. You need both Java code and lengthy XML descriptor.

JAVA:
  1. // This is Java source part
  2. public class FooDao {
  3.   HibernateTemplate hibernateTemplate;
  4.   public void setHibernateTemplate (HibernateTemplate ht) {
  5.     hibernateTemplate = ht;
  6.   }
  7.   // ... ...
  8. }

XML:
  1. <!-- This is the XML part -->
  2. <bean id="dataSource"
  3.     class="org.springframework .jndi.JndiObjectFactoryBean">
  4.   <property name="jndiname">
  5.     <value>java:comp/env/jdbc/MyDataSource</value>
  6.   </property>
  7. </bean>
  8.  
  9. <bean id="sessionFactory"
  10.     class="org.springframework.orm .hibernate.LocalSessionFactoryBean">
  11.   <property name="dataSource">
  12.     <ref bean="dataSource"/>
  13.   </property>
  14. </bean>
  15.  
  16. <bean id="hibernateTemplate"
  17.     class="org.springframework.orm .hibernate.HibernateTemplate">
  18.   <property name="sessionFactory">
  19.     <ref bean="sessionFactory"/>
  20.   </property>
  21. </bean>
  22.  
  23. <bean id="fooDao" class="FooDao">
  24.   <property name="hibernateTemplate">
  25.     <ref bean="hibernateTemplate"/>
  26.   </property>
  27. </bean>
  28.  
  29. <!-- The hibernateTemplate can be
  30.      injected into more DAO objects -->

The problem here is that Spring, being a one-size-fit-all generic DI framework, opts for a complete DI solution (e.g., naming of objects, dependence resolution, the whole 9 yards) at the expense of simplicity and elegance. I believe that for most developers, the EJB 3.0 DI solution is far superior as it addresses the most needed DI features with simplicity and does not attempt to solve the generic problem. EJB 3.0 makes it easy to inject objects for infrastructure services and commonly used application services -- and that's it. To me, that is a strength not a shortcoming..

Of course, a natural question to ask is "what if an EJB developer actually need to DI arbitrary POJOs and declare complex dependencies?" Well, like Chris mentioned, you can always inject a Spring bean factory via the JNDI. :) But in JBoss, there is a better answer: You could use our new XML-based DI microkernel in JBoss 5, which is even more generic than Spring (e.g., it supports AOP dependency) and provides all the power you would need.