11. Spring and Enterprise JavaBeans – Spring in Action, Second Edition

Chapter 11. Spring and Enterprise JavaBeans

This chapter covers

  • Wiring EJBs into a Spring context

  • Building Spring-aware EJBs

  • Using EJB 3 annotations with Spring beans

Several years ago, a series of advertisements ran on television for Reese’s peanut butter cups. In these advertisements, one character would be enjoying a chocolate bar while another would be enjoying some peanut butter. Ultimately, the characters would collide, accidentally mixing their snacks. One would proclaim, “You’ve got chocolate in my peanut butter!” while the other would declare, “You’ve got peanut butter on my chocolate!” After each would taste the mixture, they would decide it was “two great tastes that taste great together.”

You may be surprised to find a section on how to use Spring with EJBs in this book. Much of this book so far has shown you how to implement enterprise-class applications without resorting to EJBs. Many developers liken Spring and EJB more to oil and water than to chocolate and peanut butter.

The fact is that although Spring provides a lot of functionality that gives POJOs the power of EJBs, you may not always have the luxury of working on a project that is completely EJB free. On the one hand, you may have to interface with other systems that expose their functionality through stateless session EJBs. On the other hand, you may be placed in a project where for legitimate technical (or perhaps political) reasons you must write EJB code.

Whether your application is the client of an EJB or you must write the EJB itself, you don’t have to completely abandon all the benefits of Spring in order to work with EJBs. Spring offers three ways to mix the Spring and EJB to reap the benefits of both frameworks:

  • If your application will be consuming the services of a session EJB, Spring enables you to declare EJBs as beans within the Spring application context. This makes it possible to wire references to EJB session beans (both EJB 2.x and EJB 3) into the properties of your other beans as though the EJB is just another POJO.

  • If you’re developing an EJB 2.x session bean, you can write your EJB to be Spring aware. That is, your EJB will have access to the Spring application context so that it can access and use beans that are configured in Spring.

  • A Spring-related framework called Pitchfork makes it possible to use EJB 3 annotations to perform dependency injection and simple AOP on beans within the Spring context.

This chapter represents the collision of EJBs with Spring. Whether you want to write some Spring-flavored EJBs or mix EJB into your Spring application, there’s something here for you. We’ll explore all of the ways that Spring and EJB can be mixed, starting with wiring EJBs in a Spring application context.

Wiring EJBs in Spring

If you’ve ever written a client for a 2.x EJB, you are probably familiar with how you gain access to an EJB reference. First you would look up the EJB’s home interface from JNDI using code that looks a little like this:

private TrafficServiceHome trafficServiceHome;
private TrafficServiceHome getTrafficServiceHome ()
    throws javax.naming.NamingException {

  if(trafficServiceHome != null)
      return trafficServiceHome;

  javax.naming.InitialContext ctx =
      new javax.naming.InitialContext();

  try {
    Object objHome = ctx.lookup("trafficService");

    TrafficServiceHome home =
        (TrafficServiceHome) javax.rmi.PortableRemoteObject.narrow(
          objHome, TrafficServiceHome.class);

    trafficServiceHome = home;
    return home;
  } finally {
    ctx.close();
  }
}

Once you’ve got the reference to the home interface, you’ll then need to get a reference to the EJB’s business interface (either remote or local) so that you can call its methods. For example, the following code shows how you might call the getTrafficConditions() method on the traffic service EJB:

try {
  TrafficServiceHome home = getTrafficServiceHome();
  TrafficService trafficService = home.create();

  TrafficConditions conditions =
      trafficService.getTrafficConditions(city, state);
} catch (java.rmi.RemoteException e) {
  throw new TrafficException();
} catch (CreateException e) {
  throw new TrafficException();
}

Wow! That’s a lot of code just to look up traffic conditions. What’s most unsettling is that only a few lines have anything directly to do with retrieving the traffic conditions. Most of it is boilerplate EJB plumbing that is used just to retrieve a reference to the EJB. That’s an awful lot of work just to make a single call to the EJB’s getTrafficConditions() method.

EJB 3 makes things a little bit easier. Instead of looking up the EJB’s home interface from JNDI, you look up EJB 3 session beans directly from JNDI. But that still involves a lot of boilerplate JNDI lookup code.

Hold on. Throughout this book, you’ve seen ways to inject your application beans with the services they need. Beans don’t look up other beans—beans are given to other beans. But this whole exercise of looking up an EJB via JNDI and its home interface doesn’t seem to fit how the rest of the RoadRantz application is constructed. If we proceed to interact with EJB in the traditional EJB way, we’ll end up muddying up everything with ugly lookup code and will definitely couple the code with the EJB. Isn’t there a better way?

Proxying session beans (EJB 2.x)

As you’ve probably guessed from this lead-up, yes, there is a better way. In chapter 8 we showed you how to configure proxies to access various remote services, including services based in RMI, Hessian, Burlap, and Spring’s own HTTP invoker. Spring offers much the same kind of proxy support for accessing EJBs.

Spring comes with two proxies suitable for accessing session EJBs:

  • LocalStatelessSessionProxyFactoryBeanUsed to access local EJBs (EJBs that are in the same container as their clients)

  • SimpleRemoteStatelessSessionProxyFactoryBeanUsed to access remote EJBs (EJBs that are in a separate container from their clients)

As illustrated in figure 11.1, these proxy factory beans produce proxies that handle the details of looking up an EJB’s home interface and invoking the EJB’s business methods. They make it possible to configure references to EJBs in the Spring application context that can be wired as if they were any other Spring-managed bean.

Figure 11.1. Spring’s EJB proxy factory beans look up a session bean’s home interface and then produce a proxy that delegates to the actual EJB.

For illustration’s sake, let’s assume that the traffic service EJB is a local stateless session EJB. To wire a traffic service EJB in Spring, you would use LocalStatelessSessionProxyFactoryBean like this:

<bean id="trafficService"
    class="org.springframework.ejb.access.
          LocalStatelessSessionProxyFactoryBean"
    lazy-init="true">

  <property name="jndiName" value="ejb/TrafficService" />
  <property name="businessInterface"
      value="com.roadrantz.ejb.TrafficServiceEjb" />
</bean>

The jndiName property, here set to trafficService, is used to identify the name of the EJB home interface in JNDI. Meanwhile the businessInterface property identifies the EJB’s business interface. The proxy will adhere to this interface.

Pay particular attention to the lazy-init attribute on the <bean> element. The Spring application context will normally pre-instantiate singleton beans once the Spring configuration file is loaded. This is usually a good thing, but it could cause problems with EJB proxies. That’s because the Spring application context may load and instantiate the proxy before the EJB’s home interface is bound in the naming service. By setting lazy-init to true, we are telling Spring to not look up the home interface until the trafficService bean is first used—which should be plenty of time for the EJB to be bound in the naming service.

Now let’s switch gears and see how we would configure the trafficService bean if the traffic service were a remote stateless session bean. Take a close look at the following XML:

<bean id="trafficService"
    class="org.springframework.ejb.access.
           SimpleRemoteStatelessSessionProxyFactoryBean"
    lazy-init="true">

  <property name="jndiName" value="trafficService" />
  <property name="businessInterface"
      value="com.roadrantz.ejb.TrafficServiceEjb" />
</bean>

See the difference? The only thing that changed was the name of the proxy factory bean class. Nothing else needs to change. Spring makes the choice between local and remote EJBs almost transparent.

But you’re probably wondering about java.rmi.RemoteException. How can the choice between local and remote EJBs be completely transparent if invoking a remote EJB method could throw a RemoteException? Doesn’t someone need to catch that exception?

This illustrates one more benefit of using Spring’s EJB support for accessing EJBs. As with RMI services, any RemoteException that may be thrown from EJBs are caught and then rethrown as org.springframework.remoting.RemoteAccessException. Since RemoteAccessException is an unchecked exception, catching it is optional for the EJB client.

Declaring EJB proxies with Spring’s JEE namespace

Spring’s proxy factory beans for EJB access greatly simplify EJB and make it possible to wire EJBs into Spring beans as if they were any other Spring bean. Life couldn’t be much easier for EJB, could it?

Well, Spring 2 makes things even easier by supplying EJB configuration elements in the new JEE namespace. The JEE namespace includes two configuration elements specifically for EJB:

  • <jee:local-slsb>Configures a proxy to a local stateless session bean in the Spring application context

  • <jee:remote-slsb>Configures a proxy to a remote stateless session bean in the Spring application context

In order to use these two elements, you’ll need to declare the JEE namespace in your Spring configuration by including the following in the <beans> element:

<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:jee="http://www.springframework.org/schema/jee"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/
              spring-beans-2.0.xsd
         http://www.springframework.org/schema/jee
         http://www.springframework.org/schema/jee/
              spring-jee-2.0.xsd">

With the JEE namespace declared, you’re ready to use its elements to configure an EJB reference in Spring. For example, to configure a reference to a local stateless session EJB, you would use the <jee:local-slsb> as follows:

<jee:local-slsb id="trafficService"
    jndi-name="trafficService"
    business-interface="com.roadrantz.ejb.TrafficServiceEjb"/>

Under the covers, <jee:local-slsb> configures a LocalStatelessSessionProxyFactoryBean in the Spring context. The end result is the same, even though the amount of XML is less.

Similarly, a remote stateless session EJB can be wired using the <jee:remoteslsb> element:

<jee:remote-slsb id="trafficService"
    jndi-name="trafficService"
    business-interface="com.roadrantz.ejb.TrafficServiceEjb"/>

Just as <jee:local-slsb> is a shortcut for LocalStatelessSessionProxyFactoryBean, <jee:remote-slsb> is a shortcut for SimpleRemoteStatelessSessionProxyFactoryBean.

Declaring EJB 3 session beans in Spring

As I mentioned earlier, EJB 3 simplifies the session bean lookup process by eliminating the notion of a home interface. Instead of looking up a session bean through a home interface that was retrieved through JNDI, EJB 3 session beans are retrieved directly from JNDI.

But as you’ve already seen, JNDI lookup code is rather complex and is mostly boilerplate. Furthermore, looking up an EJB is in stark contrast to Spring’s principle of dependency injection. In Spring, session beans should be injected, not retrieved.

Fortunately, Spring provides the ability to wire JNDI-stored objects just like any other bean in the application context. The trick involves using Spring’s JndiObjectFactoryBean. The following snippet of XML from the Spring application context shows how you might declare an EJB 3 traffic service session bean:

<bean id="trafficService"
    class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="ejb/TrafficService" />
  <property name="resourceRef" value="true" />
</bean>

As a factory bean, JndiObjectFactoryBean produces a proxy to the real session bean (see figure 11.2), which it looks up from a JNDI repository using the jndiName property. The resourceRef property indicates that the EJB should be looked up as a Java resource, in effect prefixing the value of jndiName with java:comp/env/ before performing the lookup.

Figure 11.2. EJB 3 session beans can be configured in Spring using a JNDI proxy.

Optionally, using Spring 2.0’s JEE namespace, you can declare the EJB using the <jee:jndi-lookup>:

<jee:jndi-lookup id="trafficService"
    jndi-name="ejb/TrafficService"
    resource-ref="true" />

This snippet of XML is equivalent to the <bean> declaration above. Under the covers, <jee:jndi-lookup> creates a JndiObjectFactoryBean. We’ll talk more about JndiObjectFactoryBean and <jee:jndi-lookup> in chapter 12. But for now just know that JndiObjectFactoryBean creates a proxy to the session bean that’s stored in JNDI.

Now that the session bean proxy has been declared, it’s time to put it to work. Let’s see how to wire an EJB into a Spring-configured POJO.

Wiring EJBs into Spring beans

As it turns out, wiring an EJB into a Spring-configured POJO isn’t any different than wiring any other POJO. For example, to wire the traffic service session bean into the rantService bean, you could use the following XML:

<bean id="rantService"
    class="com.roadrantz.service.RantServiceImpl">
...
  <property name="trafficService" ref="trafficService" />
...
</bean>

Did you see that? There is nothing EJB about that. The trafficService bean (which happens to be a proxy to an EJB) is simply injected into the trafficService property. There’s no indication that EJB is involved at all. As illustrated in figure 11.3, proxied EJBs can be injected into other beans just like any other Springconfigured POJO.

Figure 11.3. EJB and JNDI proxies can be injected into a Spring beans just as if they were any other Spring bean.

The wonderful thing about using a proxy factory bean to access the traffic service EJB is that you don’t have to write your own service locator or business delegate code. In fact, you don’t have to write any JNDI code of any sort. Nor must you deal with the EJB’s home interface (or local home interface).

Furthermore, by hiding it all behind the TrafficService business interface, the trafficService bean isn’t even aware that it’s dealing with an EJB. As far as it knows, it’s collaborating with just another POJO. This is significant because it means that you are free to swap out the EJB implementation of TrafficService with any other implementation (perhaps even a mock implementation that’s used when unit testing RantServiceImpl).

Now that you’ve seen how to wire EJBs into a Spring application, let’s look at how Spring supports EJB development.

Developing Spring-enabled EJBs (EJB 2.x)

Although Spring provides many capabilities that make it possible to implement enterprise applications without EJBs, you may still find yourself needing to develop your components as EJBs.

In chapter 8, you saw how Spring exporters can turn any Spring-configured POJO into a remote service. I hate to disappoint you, but unfortunately, Spring doesn’t provide an EjbServiceExporter class that exports POJOs as EJBs. (But I do agree that such an exporter would be really cool.)

Nevertheless, Spring can make developing EJBs a little bit easier. Spring comes with four abstract support classes that bring regular EJBs into the world of Spring:

  • AbstractMessageDrivenBeanUseful for developing message-driven beans that accept messages from sources other than JMS (as allowed by the EJB 2.1 specification)

  • AbstractJmsMessageDrivenBeanUseful for developing message-driven beans that accept messages from JMS sources

  • AbstractStatefulSessionBeanUseful for developing stateful session EJBs

  • AbstractStatelessSessionBeanUseful for developing stateless session EJBs

These abstract classes simplify EJB development in two ways:

  • They provide default empty implementations of EJB lifecycle methods (e.g., ejbActivate(), ejbPassivate(), ejbRemove()). These methods are required per the EJB specification but are typically implemented as empty methods.

  • They provide access to a Spring bean factory. This makes it possible for you to implement an EJB that delegates responsibility for the business logic to Spring-configured POJOs. Effectively, the EJB can be developed as an EJB façade to Spring-managed POJO functionality.

For example, suppose that you were to expose the functionality of the rantService bean as a stateless session EJB. Listing 11.1 shows how you might implement this EJB.

Example 11.1. A stateless session EJB that delegates responsibility for business logic to a Spring-managed POJO

When the RantServiceEjb is created, its onEjbCreate() method retrieves the rantService bean from the Spring bean factory. Then, when any of its methods are invoked, they delegate responsibility to the rantService bean, as illustrated in figure 11.4

Figure 11.4. Spring’s EJB support classes enable development of EJBs that have access to a Spring application context (stored in JNDI).

The big unanswered question regarding the EJB in listing 11.1 is where the bean factory comes from. In typical JEE fashion, the abstract EJB classes retrieve the bean factory from JNDI. By default, they expect to find the Spring bean factory in JNDI with the name java:comp/env/ejb/BeanFactoryPath. This means that you’ll need to configure the bean factory in JNDI at that name.

If you’d rather configure the bean factory under a different JNDI name, set the beanFactoryLocatorKey property before the bean factory is loaded (in either the constructor or in the setSessionContext() method). For example:

public void setSessionContext(SessionContext sessionContext) {
  super.setSessionContext(sessionContext);

  setBeanFactoryLocatorKey("java:comp/env/ejb/SpringContext");
}

With this setSessionContext() method in place, the Spring context will be located using java:comp/env/ejb/SpringContext instead of the default JNDI name.

Spring’s support for developing EJBs is focused on the EJB 2.x specifications. However, the EJB 3 specification changed things dramatically. EJB 3 borrows several ideas from Spring, such as dependency injection and AOP, to make EJB development much simpler than in previous specifications. Let’s have a look at EJB 3 and how it fits into Spring.

Spring and EJB3

Although EJBs have enjoyed a great amount of popularity among Java developers since their debut, they also suffer from several complexities, including:

  • Retrieving access to an EJB involves complex JNDI code to look up the EJB’s home (or local home) interface, which is then used to create the EJB’s business interface.

  • With EJB 2.x, EJBs had to implement special interfaces that mandated that certain lifecycle callback methods be implemented. Because most applications have no use for these methods, they are often implemented as empty methods.

  • The method signatures of remote EJBs are required to throw java.rmi.RemoteException, even when the method implementation does not actually throw the exception.

These and other problems have caused many developers to lose interest in EJBs and to start looking for simpler alternatives such as Spring. Reacting to the backlash against EJBs, the Java Community Process revisited the EJB specification, producing the most significant change in EJBs since their initial introduction: the EJB 3 specification.

The EJB 3 specification addresses the concerns of its heavyweight predecessor by supporting dependency injection of EJBs and resources instead of complex JNDI lookups. However, EJB 3 encourages the use of Java 5 annotations for declaring dependencies that are to be injected into bean properties.

Moreover, EJB 3 doesn’t require that EJBs implement any specific interface or implement needless lifecycle methods. And remote methods no longer need to be declared to throw RemoteException. In short, the EJB 3 programming model is a POJO-based model.

Spring doesn’t provide any direct support for the EJB 3 specification. However, there is a Spring add-on that makes it possible to use EJB 3 annotations to perform dependency injection and AOP in Spring.

Introducing Pitchfork

Pitchfork is an add-on for Spring that supports EJB 3 annotations. It is co-developed by Interface 21 (the Spring team) and BEA and is used within BEA’s WebLogic Server 10 to support EJB 3. But you don’t have to use WebLogic to use Pitchfork. Pitchfork is open sourced under the Apache 2.0 license and can be used in any Spring 2.0 application. You can download Pitchfork from Interface 21’s site at http://www.springframework.com/pitchfork.

Pitchfork is not intended to be a complete implementation of the EJB 3 specification. However, it does support dependency injection and AOP through EJB 3 annotations, including the annotations listed in table 11.1.

Table 11.1. EJB 3 annotations supported by Pitchfork.

Annotation

What it does

@ApplicationException

Declares an exception to be an application exception, which, by default, does not roll back a transaction

@AroundInvoke

Declares a method to be an interceptor method

@EJB

Declares a dependency to an EJB

@ExcludeClassInterceptors

Declares that a method should not be intercepted by a class interceptor

@ExcludeDefaultInterceptors

Declares that a method should not be intercepted by a default interceptor

@Interceptors

Specifies one or more interceptors classes to associate with a bean class or method

@PostConstruct

Specifies a method to be executed after a bean is constructed and all dependency injection is done to perform initialization

@PreDestroy

Specifies a method to be executed prior to bean being removed from the container

@Resource

Declares a dependency to an external resource

@Stateless

Declares a bean to be a stateless session bean

@TransactionAttribute

Specifies that a method should be invoked within a transaction context

In this section, you’ll see how to use a few of these annotations within a Spring context using Pitchfork. We will assume, however, that you already have some understanding of EJB 3 and these annotations. For a more detailed discussion of EJB 3, we suggest that you have a look at EJB 3 in Action (Manning, 2006).

Getting started with Pitchfork

Pitchfork uses a bean factory postprocessor to perform dependency injection on beans that are annotated with EJB 3 annotations. Pitchfork comes with two bean factory postprocessors to choose from:

  • org.springframework.jee.config.JeeBeanFactoryPostProcessor

  • org.springframework.jee.ejb.config.JeeEjbBeanFactoryPostProcessor

For the most part, these two bean factory postprocessors are identical. Both support all of the annotations in table11.1, except for the @EJB annotation, which is only supported by JeeEjbBeanFactoryPostProcessor. If you want to use the @EJB annotation, be sure to choose JeeEjbBeanFactoryPostProcessor. Otherwise, the choice is arbitrary.

To configure one of these bean factory postprocessors in Spring, simply add it as a bean in the Spring application context. For example, to use JeeBeanFactoryPostProcessor, add the following to your Spring configuration:

<bean class="org.springframework.jee.config.
              JeeBeanFactoryPostProcessor" />

With JeeBeanFactoryPostProcessor in place, we’re now ready to start using the EJB 3 annotations. Next I’ll show you how to apply these annotations in Springmanaged beans.

Injecting resources by annotation

To illustrate the use of EJB annotations in Pitchfork, we’re going to revisit the knight example from chapter 1. Imagine that we were to rewrite the KnightOfTheRoundTable class from chapter 1 to use the @Resource attribute for dependency injection. It might look a little like listing 11.2.

Example 11.2. An annotation-injected KnightOfTheRoundTable

This KnightOfTheRoundTable is then configured in Spring using the following XML:

<bean id="knight" class=
     "com.springinaction.knight.KnightOfTheRoundTable">
  <constructor-arg value="Bedivere" />
</bean>

In chapter 1, we injected the knight’s quest property using XML in the Spring configuration. But here we’re letting the @Resource annotation do all of the work. @Resource will try to find an object named quest and, if it’s found, wire it into the quest property. Notice that there wasn’t a need for a setQuest() method—@Resource can inject directly into private properties!

But where does the quest object come from? Well, that depends. Pitchfork will first look in JNDI for an object named quest and if it finds it, that object will be wired into the quest property. If JNDI turns up nothing, it will look in the Spring application context for a bean named quest.

Therefore, you’ll either need to make sure that a Quest implementation is available through JNDI or you’ll need to declare a Spring bean named quest:

<bean id="quest"
    class="com.springinaction.knight.SlayDragonQuest" />

That demonstrates Pitchfork’s capability to do EJB 3 dependency injection. Now let’s see how Pitchfork provides for EJB 3 AOP using interceptors.

Declaring interceptors using annotations

In addition to dependency injection, Pitchfork also supports EJB 3 interceptor annotations. EJB 3 interceptors are a simple form of AOP around advice that can be applied using annotations.

For example, the Minstrel advisor from chapter 1 could be rewritten as in listing 11.3 to use the @AroundInvoke annotation.

Example 11.3. An EJB-annotated minstrel interceptor

The @AroundInvoke method declares a method that will be invoked when an advised method is intercepted. In this case, the singAboutQuest() method will be called before a target method is called so that the Minstrel can sing about the knight’s exploits.

By itself the @AroundInvoke annotation only defines an interceptor method. We still need to apply it to the KnightOfTheRoundTable class. That’s what the @Interceptors annotation is for:

@Interceptors({Minstrel.class})
public class KnightOfTheRoundTable implements Knight {
...
}

The @Interceptors annotation takes an array of one or more interceptor classes (e.g., classes that have methods that are annotated with @AroundInvoke). When a class is annotated with @Interceptors, all methods of the class are intercepted by the interceptors listed. Since KnightOfTheRoundTable only has an embarkOnQuest() method, that will be the only method intercepted. However, if you want finer control over which methods are intercepted and which are not, you may want to place the @Interceptors annotation at the method level:

@Interceptors({Minstrel.class})
public void embarkOnQuest() {
  quest.embark();
}

When used at the method level, only those methods that are annotated will be intercepted by the interceptors.

Another option for limiting classwide interception is to use the @ExcludeClassInterceptors annotation. When applied to a method, @ExcludeClassInterceptors will prevent class interceptors from intercepting the method. For example, to prevent embarkOnQuest() from being intercepted, annotate it like this:

@ExcludeClassInterceptors
public void embarkOnQuest() {
  quest.embark();
}

Pitchfork represents a choice for Spring developers. You can either use conventional Spring dependency injection and AOP, or you can use EJB 3 annotations for dependency injection and AOP. In virtually all circumstances, however, the pure Spring option is likely the best choice. Spring AOP, for instance, is far more flexible than EJB 3’s interceptors are. Nevertheless, with Pitchfork, the choice is yours to make.

Summary

Although Spring’s POJO-based development model offers a compelling alternative to Enterprise JavaBeans, there may be factors (either technical, political, or historical) that force a project to choose EJBs. In those cases, there’s no need to dismiss Spring entirely, as Spring supports both EJB development and consumption.

In this chapter, you’ve seen how Spring and EJB can work together, starting with how Spring beans can be made into clients of EJBs. Using EJB proxies, we declared references to EJBs in a Spring application context. Once configured in Spring, the EJB could then be wired into other Spring beans that will consume the EJB’s services. We also looked at how EJB proxy declaration is made simpler using Spring 2.0’s <jee:local-slsb> and <jee:remote-slsb> configuration elements.

We then turned our attention to developing EJBs. Even though Spring doesn’t provide a mechanism for directly hosting 2.x EJBs in the Spring container, Spring does provide a set of Spring-aware base classes from which EJBs can be developed. These base classes expose the Spring application context to the EJB so that the EJB can delegate its work to Spring-managed POJOs.

Finally, we peeked at Pitchfork, an intriguing Spring add-on that enables the use of EJB 3 annotations for dependency injection and AOP within a Spring container.

With the Spring-EJB story behind us, we now turn to look at a hodgepodge of other enterprise features available in Spring. In the next chapter, we’ll explore Spring’s support for JNDI, sending email, scheduling, and JMX.