Extend EJB 3 dependency injection with Spring 2.0

The general idea of extending the DI capabilities provided by Java EE 5 with those of the Spring 2.0 has already been described by Maurizio Albari in his blog post. Nevertheless there is one aspect in Maurizio's approach that I wanted to overcome: You need to have some kind of vehicle to start your Spring application context with the AspectJ BeanConfigurer? before your EJB 3 beans are initialized and this has been solved by loading the application context with an additional servlet. So that approach forces you to have a web app with at least one servlet in your ejb application.

After doing a little bit of investigation I found a solution which keeps your EJB 3 application web app free and does not introduced any API dependencies into your EJB implementations.

For now this approach is JBoss specific. Basically you need to implement an JBoss EJB 3 service extension to load the application context. A basic implementation follows:

@Service
@Management(ContextLoaderManagement.class)
public class ContextLoaderService implements ContextLoaderManagement {

    private static final Log LOG = LogFactory.getLog(ContextLoaderService.class);

    /** Currently no loading strategy implemented */
    private static final String CONTEXT_FILE_NAME = "classpath:/META-INF/applicationContext.xml";

    /** Spring BeanFactory containing the AspectJ BeanConfigurer*/
    private ConfigurableApplicationContext context = null;

    // Lifecycle methods
    public void start() throws Exception {
        LOG.info("Called start");
        this.context = new ClassPathXmlApplicationContext(CONTEXT_FILE_NAME);
    }

    public void stop() {
        LOG.info("Called stop");
        this.context.close();
    }
}

Next step is to define that your EJB stateless session bean implementation depends on the above service.

@Stateless
@Depends("jboss.j2ee:jar=userService.jar,name=ContextLoaderService,service=EJB3")
@Configurable(value="userService")
public class UserServiceImpl implements UserServiceLocal, UserServiceRemote {
    
    private static final Log LOG = LogFactory.getLog(UserServiceImpl.class);
    
    private UserDao dao;
    
    public UserServiceImpl() {
        LOG.info("Called constructor");
    }
    
    public void saveUser(User user) {
        this.dao.saveUser(user);
    }

    public User getUser(Long id) {
        return this.dao.getUser(id);
    }

    public void setDao(UserDao dao) {
        this.dao = dao;
        LOG.info("Called setDao with [" + dao + "]");
    }
}

After compiling the UserServiceImpl using the AspectJ compiler and the following applicationContext.xml your application will be configured by Spring.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

	<aop:spring-configured/>
	
	<bean id="userService" abstract="true">
		<property name="dao" ref="userDao"/>
	</bean>	
	
	<bean id="userDao" class="org.springframework.test.dao.UserDaoImpl" />
	
</beans>

This approach is certainly not as generic as I would like to have it. But as time emerges I think solutions for other EJB 3 enabled application server will come up.