Using Spring with EJB 3

Back when we were planning the migration to Glassfish, I realised we would have two dependency-injection frameworks in use – EJB 3 and Spring. For obvious reasons, I wanted to know more about how these would interact. At the time (last July), I couldn’t find anyone who had used EJB 3 and Spring together – even Ben Alex from Interface21 hadn’t come across it. Six months later, and I still haven’t heard of anyone using Spring _from_ EJBs. Except for us.


There’s lots of reasons to use both EJB 3 and Spring. EJBs have benefits Spring doesn’t give – integration with things like Servlets and JSP tags, better integration with the container for monitoring and management, tool support, and so on. Spring has benefits as well – most notably, you don’t need the container, but also you get more control over the bean lifecycle. Together, they are a good complement.

But how do you get a Spring-managed bean from an EJB? The answer: dependency injection, of course. That is, with the aid of a EJB Interceptor:

public class SpringBeanInterceptor {
  
  private BeanFactory beanFactory;
  
  @PostConstruct public void configureSpringBeans(InvocationContext context) throws Exception {
    for (Method method : context.getTarget().getClass().getMethods()) {
      if (method.isAnnotationPresent(SpringBean.class)) {
        Class springClass = method.getParameterTypes()[0];
        String springName = determineSpringBeanName(method, springClass);
        Object springBean = beanFactory().getBean(springName);
        try {
          method.invoke(context.getTarget(), springBean);
        } catch (IllegalArgumentException e) {
          throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
          throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
          throw new RuntimeException(e.getCause());
        }
      }
    }
    context.proceed();
  }
  
  private String determineSpringBeanName(Method method, Class springClass) {
    String springName = method.getAnnotation(SpringBean.class).value();
    if (springName.length() > 0) {
      springName = toCamelCase(springClass.getSimpleName());
    }
    return springName;
  }
  
  private String toCamelCase(String string) {
    return Character.toLowerCase(string.charAt(0)) + string.substring(1, string.length());
  }
  
  private BeanFactory beanFactory() {
    if (beanFactory == null) {
      BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance("classpath*:beanRefContext.xml");
      BeanFactoryReference ref = locator.useBeanFactory("applicationContext-service");
      beanFactory = ref.getFactory();
    }
    return beanFactory;
  }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringBean {
  String value() default "";
}

That’s it. This Interceptor will look over an EJB, after it’s created, and provides the Spring beans. This version looks for ‘setter-methods’ with an @SpringBean annotation (you could also change it to look for fields), and looks up a corresponding bean from the Spring context (the ContextSingletonBeanFactoryLocator makes this possible).

Here’s how you would use it:

@Stateless
@Interceptors(SpringBeanInterceptor.class)
public class MyServiceBean implements MyService {
  private MySpringBean bean;

  ...

  @SpringBean public void setMySpringBean(MySpringBean bean) {
    this.bean = bean;
  }
}

With this (and one other trick I’ll discuss soon), you can (nearly) seamless integrate EJB3 and Spring.

The one big limitation of this: with Stateless Session Beans, you can only inject stateless dependencies, and you can only inject them when the EJB is initialised.

About these ads

6 thoughts on “Using Spring with EJB 3

  1. Antoine Sabot-Durand

    Thanks for the tip.
    What’s the other trick you mention at the end of your note ?
    Do you know if there is now an official package doing this ? I’m very puzzled that no body need this…

    thanks again

    Reply
  2. Alistair Israel

    I know this article’s a bit dated, but it comes up quite often when searching for “spring ejb3″.

    Like you guys, I was trying to roll out my own Spring + EJB3 interceptor, and even marginally succeeded to finish implementing it using Spring’s own internal classes.

    Then I realized I was using Spring Framework 2.5.1, and that comes with its own SpringBeanAutowiringInterceptor specifically for this use case.

    Just thought you and others might want to know.

    Reply
  3. Robert

    I’m still on Spring 1.2.8 – so I wasn’t aware of the new features in 2.5.1. However, it’s good to see that they’ve picked up this idea (or more likely come up with it themselves).

    Thanks for the tip.

    Reply
  4. mert inan

    hi,

    this sample is very inspirational. Could you please add the full import statements of the SpringBeanInterceptor class?

    Reply
  5. kawazu

    Hi there;
    just stumbled across this searching for a solution to the same problem (initializing Spring context within an EJB module), and so far enjoyed the idea, thanks for prodiving this example. Could you however be so kind also providing the beanRefContext.xml and, more especifically, the applicationContext-service declaration referred to in

    […]
    BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(“classpath*:beanRefContext.xml”);
    BeanFactoryReference ref = locator.useBeanFactory(“applicationContext-service”);
    […] ?

    Would be much appreciated. :)
    Thanks / best regards,
    Kristian

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s