A Spring Jump Start, Part 2, Page 3
For most J2EE projects, the technology requirements will be met by either Spring or EJB. There are exceptions—your application may need to be able to support remote transaction calls. If that is the case, EJB may seem like the the way to go. Even then, Spring integrates with a Java Transaction API (JTA) transaction providers, so even this scenario is cut-and-dried. But if you are looking for a J2EE framework that provides declarative transaction management and a flexible persistence engine, Spring is a great choice. It lets you choose the features you want without the added complexities of EJB.
The complexities of EJB
So what are the complexities of EJB? Why is there such a shift toward lightweight containers? Here are a few of the complexities of EJB that turn off many developers:
- Writing an EJB is overly complicated—To write an EJB, you have to touch at least four files: the business interface, the home interface, the bean implementation, and the deployment descriptor. Other classes are likely to be involved as well, such as utility classes and value objects. That's quite a proliferation of files when all you are looking for is to add some container services to your implementation class. Conversely, Spring lets you define your implementation as a POJO and wire in any additional services needs through injection or AOP.
- EJB is invasive—This goes hand in hand with the previous point. In order to use the services provided by the EJB container, you must use the javax.ejb interfaces. This binds your component code to the EJB technology, making it difficult (if not possible) to use the component outside of an EJB container. With Spring, components are typically not required to implement, extend, or use any Spring-specific classes or interfaces, making it possible to reuse the components anywhere, even in the absence of Spring.
- Entity EJBs fall short—Entity EJBs are not as flexible or feature-rich as other ORM tools. Spring recognizes there are some great ORM tools out there, such as Hibernate and JDO, and provides a rich framework for integrating them into your application. And since an entity bean could represent a remote object, the Value Object pattern was introduced to pass data to and from the EJB tier in a course-grained object. But value objects lead to code duplication—you write each persistent property twice: once in the entity bean and once in your value object. Using Spring together with Hibernate or another ORM framework, your application's entity objects are not directly coupled with their persistence mechanism. This makes them light enough to be passed across application tiers.
Again, in most J2EE applications, the features provided by EJB may not be worth the compromises you will have to make. Spring provides nearly all of the services provided by an EJB container while allowing you to develop much simpler code. In other words, for a great number of J2EE applications, Spring makes sense. And now that you know the differences between Spring and EJB, you should have a good idea which framework fits your needs best.
1.6.2 Considering other lightweight containers
Spring is not the only lightweight container available. In the last few years, more and more Java developers have been seeking an alternative to EJB. As a result, several lightweight containers have been developed with different methods for achieving inversion of control.
Table 1.2 lists the types of IoC. These were first described with the nondescript "Type X" convention, but have since shifted to more meaningful names. We will always refer to them by the name.
Table 1.2 Inversion of Control types
|Type 1||Interface Dependent||Beans must implement specific interfaces to have their dependencies managed by the container.|
|Type 2||Setter Injection||Dependencies and properties are configured through a bean's setter methods.|
|Type 3||Constructor Injection||Dependencies and properties are configured through the bean's constructor.|
Although the focus of this book is on Spring, it may be interesting to see how these other containers stack up to Spring. Let's take a quick look at some of the other lightweight containers, starting with PicoContainer.
PicoContainer is a minimal lightweight container that provides IoC in the form of constructor and setter injection (although it favors constructor injection). We use the word minimal to describe PicoContainer because, with it small size (~50k), it has a sparse API. PicoContainer provides the bare essentials to create an IoC container and expects to be extended by other subprojects and applications. By itself, you can only assemble components programmatically through PicoContainer's API. Since this would be a cumbersome approach for anything but the most trivial applications, there is a subproject named NanoContainer that provides support for configuring PicoContainer through XML and various scripting languages. However, at the time of this writing, NanoContainer does not appear to be production-ready.
One of the limitations of PicoContainer is that it allows only one instance of any particular type to be present in its registry. This is could lead to problems if you need more than one instance of the same class, just configured differently. For example, you may want to have two instances of a javax.sql.DataSource in your application, each configured for a different database. This would not be possible in PicoContainer.
Also, you should know that PicoContainer is only a container. It does not offer any of the other powerful features that Spring has, such as AOP and third-party framework integration.
HiveMind is a relatively new IoC container. Like PicoContainer, it focuses on wiring and configuring services with support for both constructor and setter injection. HiveMind allows you to define your configuration in an XML file or in HiveMind's Simple Data Language.
HiveMind also provides an AOP-like feature with its Interceptors. This allows you to wrap a service with Interceptors to provide additional functionality. However, this is not nearly as powerful as Spring's AOP framework.
Finally, like PicoContainer, HiveMind is only a container. It provides a framework for managing components but offers no integration with other technologies.
Avalon was one of the first IoC containers developed. As with many early entrants into a market, some mistakes were made in its design. Mainly, Avalon provides interface-dependent IoC. In other words, in order for your objects to be managed by the Avalon container, they must implement Avalon-specific interfaces. This makes Avalon an invasive framework; you must change your code in order for it to be usable by the container. This is not desirable because it couples your code to a particular framework for even the simplest of cases.
We believe that if Avalon does not adopt a more flexible means of managing components, it will eventually fade out of the lightweight container market; there are other ways of achieving the same results with much less rigidity.
1.6.3 Web frameworks
Spring comes with its own very capable web framework. It provides features found in most other web frameworks, such as automatic form data binding and validation, multipart request handling, and support for multiple view technologies. We'll talk more about Spring's web framework in chapter 8. But for now, let's take a look at how Spring measures up to some popular web frameworks.
Struts can probably be considered the de facto standard for web MVC frameworks. In has been around for several years, was the first "Model 2" framework to gain wide adoption and has been used in thousands of Java projects. As a result, there is an abundance of resources available on Struts.
The Struts class you will use the most is the Action class. It is important to note that this is a class and not an interface. This means all your classes that handle input will need to subclass Action. This in contrast to Spring, which provides a Controller interface that you can implement.
Another important difference is how each handles form input. Typically, when a user is submitting a web form, the incoming data maps to an object in your application. In order to handle form submissions, Struts requires you have ActionForm classes to handle the incoming parameters. This means you need to create a class solely for mapping form submissions to your domain objects. Spring allows you to map form submissions directly to an object without the need for an intermediary, leading to eaiser maintenance.
Also, Struts comes with built-in support for declarative form validation. This means you can define rules for validating incoming form data in XML. This keeps validation logic out of your code, where it can be cumbersome and messy. Spring does not come with declarative validation. This does not mean you cannot use this within Spring; you will just have to integrate this functionality yourself using a validation framework, such as the Jakarta Commons Validator.
If you already have an investment in Struts or you just prefer it as your web framework, Spring has a package devoted to integrating Struts with Spring.
Furthermore, Struts is a mature framework with a significant following in the Java development community. Much has been written about Struts, including Ted Husted's Struts in Action (Manning, 2002).