Make the Right Decision with Our Side-by-Side Comparison of Spring and EJB 3.0, Part 2

his is the second article in a two-part series comparing Spring and EJB 3.0. The first article looked at how each technology addresses persistence, transaction management, and state. In this article I’ll continue the analysis by looking into other characteristics such as messaging, remoting, dependency management, and intermediation, and I’ll conclude by offering an overall analysis of the two technologies and several integration strategies for combining the best of both.

Messaging
Enterprise applications often don’t exist in isolation but must interact with other applications and services running within and outside a company. Messaging is a common approach to this problem and support is provided by both Spring and EJB 3.0. There are two basic operations in messaging: sending and receiving. And, typically, there are two methods of receiving messages: explicit message retrieval and automatic notification (listening).

Messaging?Functional Comparison
To explore how Spring and EJB 3.0 each implement the sending of Java Message Service (JMS) messages, consider the sequence of steps I’ve outlined for the purchasing of an airline ticket (see Figure 1).

A Spring implementation of the TsaScreener object is shown below:

public class TsaPassengerScreenerClientSpring implements TsaPassengerScreener{    private JmsTemplate jmsTemplate;    private Queue queue;    public void screenPassenger(final Passenger passenger)    {        jmsTemplate.send(queue, new MessageCreator()        {            public Message createMessage(Session session) throws JMSException            {                return session.createObjectMessage(passenger);            }        });    }     ...}

The necessary configuration to support this implementation is shown below:

                            java:comp/env/queue/tsaQueue                                java:comp/env/queue/tsaQCF                            

Compare this to the EJB 3.0 implementation:

@Statelesspublic class TsaPassengerScreenerClientEJB implements TsaPassengerScreener{    @Resource(mappedName = "java:/ConnectionFactory")    QueueConnectionFactory factory;    @Resource(mappedName = "queue/tsaQueue")    Queue queue;    public void screenPassenger(Passenger passenger)    {        try        {            QueueConnection con = factory.createQueueConnection();            QueueSession session = con.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);            ObjectMessage msg = session.createObjectMessage(passenger);            QueueSender sender = session.createSender(queue);            sender.send(msg);            session.close();            con.close();        }        catch (Exception e)        {            e.printStackTrace();        }    }}
Figure 1. Purchase Ticket Sequence Diagram. In this example the purchase ticket use case has been expanded to send a message to the TSA with the details of the passenger who is booking a flight.

Notice that the Spring implementation uses the JmsTemplate helper class to simplify working with the JMS API to send the message. Also, notice that in both cases the container injects the JMS resources (queue connection factory and queue) into the screener object. I’ll explore injection more later, in the section on dependency management. But at this point it is interesting to note the Spring configuration is more verbose then the EJB equivalent.

After the passenger details message has been sent the TSA needs to receive and process it. Both Spring and EJB 3.0 can support notification of objects when incoming messages arrive. These objects are referred to as message listeners. A Spring implementation of a message-driven POJO (MDP) is shown below:

public class TsaPassengerScreenerMDP implements MessageListener{    public void onMessage(Message message)    {        ObjectMessage objectMessage = (ObjectMessage) message;        try        {            Passenger passenger = (Passenger) objectMessage.getObject();            // Process message        }        catch (JMSException e)        {            e.printStackTrace();        }    }}

The necessary configuration to support this implementation is shown below:

                

Compare this to the EJB 3.0 implementation of a message-driven bean (MDB):

@MessageDriven(activationConfig = {        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/tsaQueue") })public class TsaPassengerScreenerMDB implements MessageListener{    public void onMessage(Message message)    {        ObjectMessage objectMessage = (ObjectMessage) message;        try        {            Passenger passenger = (Passenger) objectMessage.getObject();            // Process message        }        catch (JMSException e)        {            e.printStackTrace();        }    }}

As shown above, both Spring and EJB 3.0 enable the sending and receiving of JMS messages. And interestingly the implementations of message consumption via a Spring MDP and an EJB MDB are quite similar. Additionally, both solutions allow the volume of concurrent message processing to be throttled allowing you to tune the amount of resources allocated within an application to message processing. In the case of EJBs this is a container-specific function whereas with Spring it is a function of the message listener container.

Another important feature of enterprise messaging solutions is their ability to participate in distributed transactions. Often multiple resources will be involved in message processing (typically messaging and database resources) and the work performed on these resources should be performed atomically. Both Spring and EJB 3.0 allow messaging activities to participate in distributed transactions. In EJB 3.0 however, in order to have message acknowledgement tied to successful completion of a transaction, you will need to use container-managed transactions, otherwise messages that have been previously processed may be redelivered. The same can be accomplished in Spring when using JTA transactions and when the message container is defined as “sessionTransacted.” In either case you could choose to allow message redelivery and handle this case programmatically.

A more telling difference in messaging support between Spring and EJB is the type of objects that can be configured to listen for incoming messages. In EJB message listeners must be EJB components whereas in Spring any POJO can potentially be activated in response to incoming messages. Both require that the message listener implement an interface and both support the javax.jms.MessageListener interface for JMS message listeners. However, since the 2.1 version of the EJB specification EJB MDBs can support other messaging interfaces such as javax.resource.cci.MessageListener which allow MDBs to be activated by a JCA CCI connector.

Messaging?Non-Functional Comparison
Most Java applications use JMS to interact with an underlying message provider. This is the case with both Spring and EJB 3.0. But Spring provides templates that assist with retrieving the necessary message resources from the application server (via JNDI) and for creating, sending, and receiving messages. EJB 3.0 allows message resources to be injected into a class via its dependency injection facilities, but it does not provide any facility equivalent to Spring’s template to simplify the use of the JMS API. Spring also provides message converters which can automate the conversion of Java objects to message content.

In terms of standardization both approaches support the Java standard for messaging, JMS. In terms of implementation however MDBs are a JCP standard whereas Spring MDPs are not. But although MDPs are not a standard they are portable as the Spring runtime can run in any JSE or JEE container.

Messaging?Summary
Table 1 summarizes the key differences between Spring and EJB 3.0’s messaging support.

Table 1. Messaging Features in Spring and EJB 3.0.

Feature Spring EJB 3.0
Message Listening ? ?
Listener Types Any POJOimplementing the javax.jms.MessageListener interface MDBs implementing any interface appropriate to its messaging type, usually javax.jms.MessageListener
Message Throttling ? ? (Container specific)
Messaging Support ? (Templates, Message Converters) — (No support above the JMS API)
Distributed Transaction Support ? ?

Remoting
Whereas messaging typically involves asynchronous messages being exchanged between systems, remoting is the ability to make synchronous requests between systems. This is useful in a variety of cases, for instance, in the context of my flight booking application, to perform authorization against a passenger’s credit card. The most popular mechanisms for remoting today are Web services and RMI. Web services requests are typically transported via HTTP and as such are routable through corporate firewalls. Their message structure is XML and as such is not platform- or programming language- specific. RMI on the other hand is typically used for communication between Java applications and is generally not routable through firewalls.

Remoting?Functional Comparison
For this comparison consider the role the booking agent plays in coordinating the purchase of airline tickets (see Figure 2).

Figure 2. Remote Booking Agent. In this example the booking agent illustrated in the first article is distributed; clients interact with the agent unaware that it is running on a remote server.

The test for this use case is listed in the first article. It can be satisfied using Spring to implement a remote booking agent. In this implementation nothing must be done to the Spring booking agent to make it accessible remotely. All that must be done is to export the agent using the RmiServiceExporter as illustrated below:

                            

To make the EJB 3.0 version of the booking agent remote all that must be done to it is to annotate it as a remote component as shown below:

@[email protected] class BookingAgentEJB implements BookingAgent{    ...}

Notice that in both cases neither the interface nor logic of the booking agent had to change to expose the agent to remote callers. In fact, the Spring implementation did not require any changes to the booking agent at all! The same result could have been achieved with EJB 3.0 if I had decided to use XML instead of annotations to declare the component as remote.

Remoting?Non-Functional Comparison
EJB remoting is built on RMI and makes creating distributed Java applications easy, including support for security and transaction propagation. Stateless EJBs may also be exposed as Web services using the metadata annotations defined in JSR 181 (Web Services Metadata for the Java Platform).

Spring 2.0 supports various forms of remoting including RMI, Web services, Hessian, Burlap, and HTTP invokers (using JDK serialization). It is differentiated from EJB in that any POJO may be exposed as a remote object. This is implemented through a combination of Spring helper classes and configuration. But Spring remoting does not support security and transaction propagation as EJB remoting does.

Remoting?Summary
Table 2 summarizes the differences between remoting support in Spring and EJB 3.

Table 2. Remoting Support in Spring and EJB 3.0

Feature Spring EJB 3.0
Supported Protocols RMI, Web Services, Hessian, Burlap, HTTP RMI, Web Services
Expose remote objects ? (Any POJO) ? (EJBs only)
Instance pooling and load balancing ?
Transaction Propagation ?
Security Propagation ?

Dependency Management
Dependency management refers to the ability to control the communication between components in a system. By externalizing (or “wiring”) these interdependencies designers intend to create more loosely coupled systems. Systems with looser coupling are generally easier to extend, test, and maintain. Externalizing dependency management is also referred to as inversion of control (IOC) and the act of injecting dependencies into a class is referred to as dependency injection.

There are three primary forms of dependency injection: setter, constructor, and field injection. For a discussion of setter versus constructor injection see “Inversion of Control Containers and the Dependency Injection Pattern” by Martin Fowler. The value of field injection is debatable. It is certainly convenient when using JEE’s dependency injection annotations but it violates the principle of encapsulation and makes it more difficult to unit test classes.

Spring and EJB 3.0 both offer support for dependency management but implement different types of dependency injection. Spring supports both setter- and constructor-based injection. EJB 3.0 supports setter and field injection. Neither is a limiting decision, but this subtle difference should be kept in mind when considering Spring and EJB 3.0’s support for dependency management.

One of the core features of the Spring framework is its IOC framework and so, not surprisingly, it offers much more mature and configurable support for dependency management then does EJB 3.0. In Spring dependencies are generally mapped (or “wired”) via XML configuration files. The primary mechanism for wiring in JEE 5?and in EJB 3.0 specifically?is via source code annotations. Annotations have the advantage of being closer to the code they modify and thus can improve understandability of the system. On the other hand they create compile-time and runtime dependencies on their implementation and thus compromise the portability of annotated classes.

In Spring, applications dependencies are most typically configured via XML:

        

By contrast, with JEE 5 and EJB 3.0, dependency injection often takes the form of annotations:

@[email protected] class BookingAgentEJB extends AbstractStatefulBookingAgent implements BookingAgent{    @EJB    private FlightDAO flightDAO;    @EJB    private TicketDAO ticketDAO;    ...}

In larger applications with more interdependencies and resource requirements wiring can be an arduous task. The ability of the dependency injection container to implicitly wire dependencies is referred to as auto-wiring. Both Spring and EJB 3.0 have the ability to wire dependencies by both name and type.

A unique feature to Spring is its ability to invoke factory beans to create object instances. This allows object factories to be invoked to create object instances for injection. It can give developers programmatic control of the object creation process as well as legacy code to be adapted to the Spring dependency management mechanism.

The power of dependency management in the Spring framework is in its ability to manage any object, to integrate with legacy code bases, and to be highly extensible and configurable. The EJB 3.0 dependency management facility is useful, but not nearly as robust as the one offered by Spring. Table 3 summarizes the differences in support for dependency management between Spring and EJB 3.0:

Table 3. Dependency Management Features in Spring and EJB 3.0.

Feature Spring EJB 3.0
Setter Injection ? ?
Constructor Injection ?
Field Injection ?
Autowiring ? (by name and type) ? (by name and type)
Factory Beans ?
Factory Methods ?
JNDI Injection ? ?
Configuration Primarily XML Primarily Annotations
Scope Any Object Specific JEE types

Intermediation
Intermediation means coming between two things in time, place, or order. It is an important concept in enterprise software development as intermediation can address problems not easily solved through traditional object design. One example might be security. In an application where policy requires authentication for all system operations, an object oriented approach that encapsulates that authentication information in a dedicated object makes sense. But programmers must still remember to delegate to this component in each new system operation they create. Alternatively this policy can be applied to the application outside of the code through techniques such as filters, interceptors, or full-blown aspect-oriented programming (AOP). Issues such as the security example I just gave are frequently referred to as crosscutting concerns because they cut through many points in an application. Intermediation can be described as a technique to modularize such concerns.

As AOP is a core feature of Spring it is not surprising that it offers rich capabilities in this area. Spring allows additional behavior to be applied to an application through several mechanisms. In Spring 1.2 such behavior was described through XML configuration files where pointcuts, advice, and advisors were defined and wired into an application. Spring 2.0 provides tight integration with AspectJ and allows aspects to be defined via annotations or via XML.

AspectJ is a powerful AOP framework allowing you to control almost all facets of your application. For more information on implementing AOP techniques consult Brett Schuchert’s excellent wiki page.

As EJB 3.0 was influenced greatly by the success of Spring, the specification has taken a first step in the direction of intermediation by introducing the concept of method interception. Interceptors allow custom code to execute around EJB methods. Interceptors can be either special methods in an EJB or in a separate class entirely. Interceptors can be mapped to methods either via annotations or XML. Interceptors are nowhere near as robust as Spring AOP, especially when coupled with AspectJ, but they do provide very useful functionality in a specification-compliant manner.

Table 4 summarizes the differences in intermediation support offered by Spring and EJB 3.0:

Table 4. Intermediation Features in Spring and EJB 3.0.

Feature Spring EJB 3.0
Method Interception ? ?
Scope Any Object Only EJBs
Pointcuts ?
Introductions ?
Integrations AspectJ
Standards AOP Alliance JCP

Integration Strategies
As has been seen throughout this article, Spring and EJB 3.0, while being different in nature, address many of the same concerns. As should be expected, each technology addresses these concerns differently and each has its own advantages and disadvantages. There will certainly be some situations where you will want to wholly adopt one technology over the other. But you should also consider blending the strengths of each.

Here are some strategies for doing this:

  • Use the Java Persistence API in your Spring application?Spring 2.0 supports the Java Persistence API allowing you to take advantage of this persistence standard in your Spring application
  • Inject EJB 3 components into your Spring application?This is much simpler as of EJB 3 and can be performed using Spring’s JndiObjectFactoryBean
  • Inject Spring beans into EJB 3 components?This can be accomplished using EJB 3 interceptors or another AOP mechanism. The JBoss Spring deployer (http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossSpringIntegration) is an example of this.
  • Use Pitchfork to allow elements of EJB 3 to be used within Spring?Pitchfork (http://www.interface21.com/pitchfork) provides a Spring-based implementation of some elements of EJB 3 including interceptors, injection, and declarative transaction management.

Another approach that could be appropriate depending on the circumstances is to reinforce the shortcomings of EJB with vendor-specific functionality. For example, the JBoss application server offers robust AOP and dependency injection on top of its EJB 3.0 implementation.

The Bottom Line
EJB 3.0 has succeeded in its goal of simplifying the development of enterprise components while continuing to offer many useful services to support the development of enterprise Java applications. However, as we have seen, the Spring framework succeeds in offering comparable services while still providing great flexibility in development of powerful, flexible, and testable enterprise applications.

In the first article I compared Spring and EJB 3.0 in terms of persistence, transaction management, and statefulness. I highlighted support for statefulness, flexibility, configurability, and standardization as the more characteristic differences between the two frameworks. In this article I explored support for messaging, remoting, dependency management, and intermediation.

Spring and EJB 3.0 offer fairly comparable support for messaging. Message-driven beans are a proven and important part of EJB. By contrast, message-driven POJOs are a new addition to Spring. But Spring does offer additional support for sending JMS messages in the form of templates and message converters.

EJB 3.0 offers stronger support for remoting then the Spring framework. In fact, Rod Johnson’s book “J2EE Development without EJB” acknowledges that distribution is one of the things that EJB does well and provides arguably the strongest case for EJB. In part 1 of this article I mentioned that EJB defines itself as a distributed component architecture. As distribution is key to what EJB is it is not surprising that it excels in this area. In many applications however remoting is an unnecessary complexity. If remoting is an important part of your application then you should strongly consider EJB.

Dependency management and intermediation are strong aspects of the Spring framework. In fact, Spring’s flexibility and extensibility are due largely to Spring’s strength in these two areas. EJB 3.0’s support for dependency management and intermediation pale in comparison to Spring’s. The value of Spring’s support for dependency management and intermediation cannot be understated, particularly in the areas of testability and runtime configurability.

Additionally, it should be noted that Spring can more easily be configured to run in a wider array of environments. For example, EJB 3.0 requires Java 5 while Spring does not. As many large corporations have not yet made the move to Java 5 this is an important distinction. EJB 3.0 requires an EJB container (although this container may be embedded), while Spring can run in a JSE environment. EJB 3.0 also has additional packaging and deployment requirements that Spring does not. There is no doubt that Spring is more configurable and flexible than EJB 3.0, but it does require experience and expertise to be used effectively. Of course, enterprise development always requires skill as these types of applications are among the more complex, and valuable, ones being produced today.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Related Posts