ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

POJO Application Frameworks: Spring Vs. EJB 3.0
Pages: 1, 2

Declarative Services

Spring and EJB 3.0 wire runtime services (such as transaction, security, logging, messaging, and profiling services) to applications. Since those services are not directly related to the application's business logic, they are not managed by the application itself. Instead, the services are transparently applied by the service container (i.e., Spring or EJB 3.0) to the application at runtime. The developer (or administrator) configures the container and tells it exactly how/when to apply services.



EJB 3.0 configures declarative services using Java annotations, while Spring uses XML configuration files. In most cases, the EJB 3.0 annotation approach is the simpler and more elegant way for this type of services. Here is an example of applying transaction services to a POJO method in EJB 3.0.


public class Foo {
    
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public bar () {
      // do something ...
    }    
}

You can also declare multiple attributes for a code segment and apply multiple services. This is an example of applying both transaction and security services to a POJO in EJB 3.0.


@SecurityDomain("other")
public class Foo {
    
    @RolesAllowed({"managers"})
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public bar () {
      // do something ...
    }   
}

Using XML to specify code attributes and configure declarative services could lead to verbose and unstable configuration files. Below is an example of XML elements used to apply a very simple Hibernate transaction to the Foo.bar() method in a Spring application.


<!-- Setup the transaction interceptor -->
<bean id="foo" 
  class="org.springframework.transaction
        .interceptor.TransactionProxyFactoryBean">
    
    <property name="target">
        <bean class="Foo"/>
    </property>
    
    <property name="transactionManager">
        <ref bean="transactionManager"/>
    </property>
    
    <property name="transactionAttributeSource">
        <ref bean="attributeSource"/>
    </property>
</bean>

<!-- Setup the transaction manager for Hibernate -->
<bean id="transactionManager" 
  class="org.springframework.orm
         .hibernate.HibernateTransactionManager">
    
    <property name="sessionFactory">
        <!-- you need to setup the sessionFactory bean in 
             yet another XML element -- omitted here -->
        <ref bean="sessionFactory"/>
    </property>
</bean>

<!-- Specify which methods to apply transaction -->
<bean id="transactionAttributeSource"
  class="org.springframework.transaction
         .interceptor.NameMatchTransactionAttributeSource">
  
    <property name="properties">
        <props>
            <prop key="bar">
        </props>
    </property>
</bean>

The XML complexity would grow geometrically if you added more interceptors (e.g., security interceptors) to the same POJO. Realizing the limitations of XML-only configuration files, Spring supports using Apache Commons metadata to specify transaction attributes in the Java source code. In the latest Spring 1.2, JDK-1.5-style annotations are also supported. To use the transaction metadata, you need to change the above transactionAttributeSource bean to an AttributesTransactionAttributeSource instance and add additional wirings for the metadata interceptors.


<bean id="autoproxy"
    class="org.springframework.aop.framework.autoproxy
           .DefaultAdvisorAutoProxyCreator"/>
<bean id="transactionAttributeSource"
    class="org.springframework.transaction.interceptor
           .AttributesTransactionAttributeSource"
    autowire="constructor"/>
<bean id="transactionInterceptor"
    class="org.springframework.transaction.interceptor
           .TransactionInterceptor"
    autowire="byType"/>
<bean id="transactionAdvisor"
    class="org.springframework.transaction.interceptor
           .TransactionAttributeSourceAdvisor"
    autowire="constructor"/>
<bean id="attributes"
    class="org.springframework.metadata.commons
           .CommonsAttributes"/>

The Spring metadata simplifies the transactionAttributeSource element when you have many transactional methods. But it does not solve the fundamental problems with XML configuration files--the verbose and fragile transaction interceptor, transactionManager, and transactionAttributeSource are all still needed.

Dependency Injection

A key benefit of middleware containers is that they enable developers to build loosely coupled applications. The service client only needs to know the service interface. The container instantiates service objects from concrete implementations and make them available to clients. This allows the container to switch between alternative service implementations without changing the interface or the client-side code.

The Dependency Injection pattern is one of best ways to implement loosely coupled applications. It is much easier to use and more elegant than older approaches, such as dependency lookup via JNDI or container callbacks. Using DI, the framework acts as an object factory to build service objects and injects those service objects to application POJOs based on runtime configuration. From the application developer's point of view, the client POJO automatically obtains the correct service object when you need to use it.

Both Spring and EJB 3.0 provide extensive support for the DI pattern. But they also have some profound differences. Spring supports a general-purpose, but complex, DI API based upon XML configuration files; EJB 3.0 supports injecting most common service objects (e.g., EJBs and context objects) and any JNDI objects via simple annotations.

The EJB 3.0 DI annotations are extremely concise and easy to use. The @Resource tag injects most common service objects and JNDI objects. The following example shows how to inject the server's default DataSource object from the JNDI into a field variable in a POJO. DefaultDS is the JNDI name for the DataSource. The myDb variable is automatically assigned the correct value before its first use.


public class FooDao {

    @Resource (name="DefaultDS")
    DataSource myDb;
    
    // Use myDb to get JDBC connection to the database
}

In addition to direct field variable injection, the @Resource annotation in EJB 3.0 can also be used to inject objects via a setter method. For instance, the following example injects a session context object. The application never explicitly calls the setter method--it is invoked by the container before any other methods are called.


@Resource 
public void setSessionContext (SessionContext ctx) { 
    sessionCtx = ctx; 
} 

For more complex service objects, special injection annotations are defined. For instance, the @EJB annotation is used to inject EJB stubs and the @PersistenceContext annotation is used to inject EntityManager objects, which handle database access for EJB 3.0 entity beans. The following example shows how to inject an EntityManager object into a stateful session bean. The @PersistenceContext annotation's type attribute specifies that the injected EntityManager has an extended transaction context--it does not automatically commit with the JTA transaction manager, and hence it can be used in an application transaction that spans across multiple threads in a session.


@Stateful
public class FooBean implements Foo, Serializable {

    @PersistenceContext(
      type=PersistenceContextType.EXTENDED
    )
    protected EntityManager em;
    
    public Foo getFoo (Integer id) {
        return (Foo) em.find(Foo.class, id);
    }
}

The EJB 3.0 specification defines server resources that can be injected via annotations. But it does not support user-defined application POJOs to be injected into each other.

In Spring, you first need to define a setter method (or constructor with arguments) for the service object in your POJO. The following example shows that the POJO needs a reference to the Hibernate session factory.


public class FooDao {
    
    HibernateTemplate hibernateTemplate;
    
    public void setHibernateTemplate (HibernateTemplate ht) {
        hibernateTemplate = ht;
    }
    
    // Use hibernateTemplate to access data via Hibernate
    public Foo getFoo (Integer id) {
        return (Foo) hibernateTemplate.load (Foo.class, id);
    }
}

Then, you can specify how the container gets the service object and wire it to the POJO at runtime through a chain of XML elements. The following example shows the XML element that wires a data source to a Hibernate session factory, the session factory to a Hibernate template object, and finally, the template object to the application POJO. Part of the reason for the complexity of the Spring code is the fact that we need to inject the underlying Hibernate plumbing objects manually, where the EJB 3.0 EntityManager is automatically managed and configured by the server. But that just brings us back to the argument that Spring is not as tightly integrated with services as EJB 3.0 is.


<bean id="dataSource" 
  class="org.springframework
         .jndi.JndiObjectFactoryBean">
    <property name="jndiname">
        <value>java:comp/env/jdbc/MyDataSource</value>
    </property>
</bean>

<bean id="sessionFactory" 
  class="org.springframework.orm
         .hibernate.LocalSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource"/>
    </property>
</bean>

<bean id="hibernateTemplate" 
  class="org.springframework.orm
         .hibernate.HibernateTemplate">
    <property name="sessionFactory">
        <ref bean="sessionFactory"/>
    </property>    
</bean>

<bean id="fooDao" class="FooDao">
    <property name="hibernateTemplate">
        <ref bean="hibernateTemplate"/>
    </property>
</bean>

<!-- The hibernateTemplate can be injected
        into more DAO objects -->

Although the XML-based Dependency Injection syntax in Spring is complex, it is very powerful. You can inject any POJO, including the ones defined in your applications, to another POJO. If you really want to use Spring's DI capabilities in EJB 3.0 applications, you can inject a Spring bean factory into an EJB via the JNDI. In some EJB 3.0 application servers, the vendor might define extra non-standard APIs to inject arbitrary POJOs. A good example is the JBoss MicroContainer, which is even more generic than Spring, as it handles Aspect-Oriented Programming (AOP) dependencies.

Conclusions

Although Spring and EJB 3.0 both aim to provide enterprise services to loosely coupled POJOs, they use very different approaches to archive this goal. Dependency Injection is a heavily used pattern in both frameworks.

With EJB 3.0, the standards-based approach, wide use of annotations, and tight integration with the application server all result in greater vendor independence and developer productivity. With Spring, the consistent use of dependency injection and the centralized XML configuration file allow developers to construct more flexible applications and work with several application service providers at a time.

Acknowledgments

The author would like to thank Stephen Chambers, Bill Burke, and Andy Oliver for valuable comments.

Resources

Michael Juntao Yuan specializes in lightweight enterprise / web application, and end-to-end mobile application development.


Return to ONJava.com.