I am about to publish a detailed hands-on guide for migrating a legacy Enterprise Javabeans (EJB) version 1 or 2 application to a Spring Framework + Hibernate stack. While the guide will concentrate on providing a concrete example of such a migration, I will here give a brief discussion on why and when such a migration would be justified.
For a more authoritative source, I recommend Rod Johnson’s excellent books such as J2EE Design and Development and J2EE Development without EJB. The Spring Framework grew out of Rod’s work outlined in J2EE Design and Development.
Now (in year 2007) everybody agrees that EJB specification versions 1 (from year 1998) and 2 (2001) have inherent problems. It is delightful that EJB 3 (2006) has addressed many shortcomings of the earlier versions, often in ways suggested by leading open source frameworks, though proprietary open source solutions keep on evolving more rapidly and maybe a bit more pragmatically than the standard.
A brief genealogy of the earlier EJB versions can be found in a good post by Floyd Marinescu. The article underlines perhaps the biggest problem of the legacy EJB versions: the EJB 1 specification aims to make every EJB component reusable, even remotely over a network connection. While this provides little value in most enterprise cases, it adds complexity and API clutter. To achieve the questionable goal, EJB components have their lifecycle completely managed by the EJB container, which makes their out-of-container use nearly impossible.
I remember how using EJBs in place of direct RMI seemd to make perfect sense when I was reading Enterprise JavaBeans (third edition) by Richard Monson-Haefel. I had just been working on an RMI project for some months, and like Monson-Haefel, thought I could see how EJBs might have made our project easier. Monson-Haefel’s definition of EJB was
Enterprise JavaBeans is a standard server-side component model for component transaction monitors. (p. 5)
But then I became slightly confused when seeing that in practice, session EJBs were often used on systems that either were or should have been collocated. And entity EJBs seemed stranger still, as the object-relational mapping of container-managed persistence (CMP) was very cumbersome, and bean-managed persistence did not seem to offer any added value over the traditional way of persisting POJOs with JDBC.
The old EJB approach also gave false promises of being able to perfectly automate and abstract some fundamental programming issues, much in line with the RAD tools promises of making programming trivial:
EJB is designed to make it easy for developers to create applications, freeing them from low-level system details of managing transactions, threads, load balancing, and so on. Application developers can concentrate on business logic and leave the details of managing the data processing to the framework.
“A beginner’s guide to Enterprise JavaBeans”, By Mark Johnson, JavaWorld.com, 10/01/98
More problems arise from the classloader scheme of EJB. While good in some cases, in a typical collocated, single-application case, the separation of EJB classes and their transitive dependencies to another classloader invisible to e.g. the web application classloader just makes development-time deployments slower than they would be without EJBs. This reduces developer productivity drastically.
Lately Test-Driven Development has made good progress in making its way to a recommended practice in software development. Old EJB standards have the problem that especially entity beans are impossible to construct outside the container, making unit testing hardly possible. Also using service lookups (from JNDI) instead of dependency injection makes unit testing EJB code complicated; consider
PersonDao mockDao = mock(PersonDao.class); PersonFinderBean finder = new PersonFinderBean(mockDao);
MockContextFactory.setAsInitial(); Context ctx = new InitialContext(); MockContainer mc = new MockContainer(ctx); SessionBeanDescriptor dd = new SessionBeanDescriptor("java:comp/env/ejb/PersonDaoEjb", PersonDao.class, PersonDao.class, mock(PersonDao.class))) ; mc.deploy(dd); PersonFinderBean finder = new PersonFinderBean();
(I adapted the latter example from “Streamlining Your EJB Tests With MockEJB” from BEA dev2dev, by Eoin Woods and Alexander Ananiev 10/17/2005.)
These two kinds of inversion of control, service locators versus dependency injection, are discussed in the classic article by Martin Fowler and EJB 3 has adopted dependency injection in favor of JNDI lookups from client code.
Whereas the new EJB 3 specification fortunately addresses many of the problems of the easier versions, my hunch is that given no political constraints, Spring + Hibernate is still bound to be more productive, because of the greater flexibility and speed of development than EJB. For attaining a more objective view, there are a lot of more or less slanted EJB 3 and Spring comparisons around, such as
- “POJO Application Frameworks: Spring Vs. EJB 3.0” by Michael Juntao Yuan on June 29, 2005
- “Make the Right Decision with Our Side-by-Side Comparison of Spring and EJB 3.0” by Rod Coffin on August 31, 2006; A DevX article but I link to InfoQ for extra comments
- “Spring, EJB 3, and the future” by Dion Almaer
- Rod Johnson comments on the relationship of Spring and EJB 3.0 on TheServerSide.com
Two things are certain:
- EJB 1 and 2 have widely acknowledged problems. Whether or not these problems warrant reacting to them depends on the case. A working solution must not be tampered with just for the sake of it. Often it makes sense to improve it in conjunction with other changes, such as delivering new, useful functionality or fixing bugs.
- EJB 3 and Spring / Hibernate both provide an approach radically different from the EJB 1 or 2 world. If you suffered with EJB 2, you shouldn’t discard EJB 3 because of that. But it’s always worthwhile to check out proprietary, more independent open source solutions in addition to the standard.