Using Spring with EJB 3
By Robert. Filed in Java |
Tags: ejb , ioc , spring
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:
[source='java']
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 “”;
}
[/source]
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:
[source='java']
@Stateless
@Interceptors(SpringBeanInterceptor.class)
public class MyServiceBean implements MyService {
private MySpringBean bean;
…
@SpringBean public void setMySpringBean(MySpringBean bean) {
this.bean = bean;
}
}
[/source]
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.