http://www.developer.com/

Back to article

A Spring Jump Start, Part 2


April 27, 2005

[Editor's Note: This piece picks up where the article A Spring Jump Start left off.]

1.5 Applying aspect-oriented programming

While inversion of control makes it possible to tie software components together loosely, aspect-oriented programming enables you to capture functionality that is used throughout your application in reusable components.

1.5.1 Introducing AOP

Aspect-oriented programming is often defined as a programming technique that promotes separation of concerns within a software system. Systems are composed of several components, each responsible for a specific piece of functionality. Often, however, these components also carry additional responsibility beyond their core functionality. System services such as logging, transaction management, and security often find their way into components whose core responsibility is something else. These system services are commonly referred to as cross-cutting concerns because they tend to cut across multiple components in a system.

By spreading these concerns across multiple components, you introduce two levels of complexity to your code:

  • The code that implements the systemwide concerns is duplicated across multiple components. This means that if you need to change how those concerns work, you'll need to visit multiple components. Even if you've abstracted the concern to a separate module so that the impact to your components is a single method call, that single method call is duplicated in multiple places.
  • Your components are littered with code that isn't aligned with their core functionality. A method to add an entry to an address book should only be concerned with how to add the address and not with whether it is secure or transactional.

Figure 1.4 illustrates this complexity. The business objects on the left are too intimately involved with the system services. Not only does each object know that it is being logged, secured, and involved in a transactional context, but also each object is responsible for performing those services for itself.



Click here for a larger image.

Figure 1.4 Calls to system-wide concerns such as logging and security are often scattered about in modules where those concerns are not their primary concern.

AOP makes it possible to modularize these services and then apply them declara-tively to the components that they should affect. This results in components that are more cohesive and that focus on their own specific concerns, completely igno-rant of any system services that may be involved.

As shown in figure 1.5, it may help to think of aspects as blankets that cover many components of an application. At its core, an application is comprised of modules that implement the business functionality. With AOP, you can then cover your core application with layers of functionality. These layers can declaratively be applied throughout your application in a flexible manner without your core application even knowing they exist. This is a very powerful concept.

Using AOP, systemwide concerns blanket the components that they impact.

Figure 1.5 Using AOP, systemwide concerns blanket the components that they impact.

1.5.2 AOP in action

Let's revisit our knight example to see how AOP works with Spring. Suppose that after showing your progress to marketing, they came back with an additional requirement. In this new requirement, a minstrel must accompany each knight, chronicling the actions and deeds of the knight in song.5

To start, you create a Minstrel class:

package com.springinaction.chapter01.knight;

import org.apache.log4j.Logger;

public class Minstrel {
   Logger song = Logger.getLogger(KnightOfTheRoundTable.class);
   public Minstrel() {}

   public void compose(String name, String message) {
      song.debug("Fa la la! Brave " + name + " did " + message + "!");
   }
}

In keeping with the IoC way of doing things, you alter KnightOfTheRoundTable to be given an instance of Minstrel:

public class KnightOfTheRoundTable {
...
   private Minstrel minstrel;
   public void setMinstrel(Minstrel minstrel) {
      this.minstrel = minstrel;
   }

...

   public HolyGrail embarkOnQuest() throws QuestException {
      minstrel.compose(name, "embark on a quest");
      return quest.embark();
   }
}

There's only one problem. As it is, each knight must stop and tell the minstrel to compose a song before the knight can continue with his quest (as in figure 1.6). Ideally a minstrel would automatically compose songs without being explicitly told to do so. A knight shouldn't know (or really even care) that their deeds are being written into song. After all, you can't have your knight being late for quests because of a lazy minstrel.

Without AOP, a knight must tell his minstrel to compose songs.

Figure 1.6 Without AOP, a knight must tell his minstrel to compose songs.

In short, the services of a minstrel transcend the duties of a knight. Another way of stating this is to say that a minstrel's services (song writing) are orthogonal to a knight's duties (embarking on quests). Therefore, it makes sense to implement a minstrel as an aspect that adds its song-writing services to a knight. Probably the simplest way to create an aspect-oriented minstrel is to change the minstrel class to be an implementation of MethodBeforeAdvice, as shown in listing 1.11.

Listing 1.11 An aspect-oriented minstrel



Click here for a larger image.

As a subclass of MethodBeforeAdvice, the MinstrelAdvice class will intercept calls to the target object's methods, giving the before() method an opportunity to do something before the target method gets called. In this case, MinstrelAdvice naively assumes that the target object is a KnightOfTheRoundTable and uses log4j as its mechanism for chronicling the knight's actions. As illustrated in figure 1.7, the knight needn't worry about how he is being sung about or even that the minstrel is writing the song.

Figure 1.7 An aspect-oriented minstrel covers a knight, chronicling the knight's activities without the knight's knowledge of the minstrel.

The knight no longer needs to tell this new aspect-oriented minstrel to sing about the knight's activities. In fact, the knight doesn't even need to know that the minstrel exists. But how does MinstrelAdvice know that it is supposed to intercept calls to a Knight?

Weaving the aspect

Notice that there's nothing about MinstrelAdvice that tells the Minstrel what object it should sing about. Instead, a Minstrel's services are applied to a Knight declaratively. Applying advice to an object is known as weaving. In Spring, aspects are woven into objects in the Spring XML file, much in the same way that beans are wired together. Listing 1.12 shows the new knight.xml, modified to weave MinstrelAdvice into a KnightOfTheRoundTable.

Listing 1.12 Weaving MinstrelAdvice into a knight



Click here for a larger image.

Notice that the id of KnightOfTheRoundTable has changed from knight to knightTarget and now knight points to a Spring class called ProxyFactoryBean. What this means is that when the container is asked for a knight object, it will return an object that intercepts calls to the target KnightOfTheRoundTable object, giving MinstrelAdvice a shot at handling method calls first. Once MinstrelAdvice is finished, control is returned to KnightOfTheRoundTable to perform the knightly task.

Don't worry if this doesn't make sense yet. We'll explain Spring's AOP support in more detail in chapter 3. For now, suffice it to say that even though a knight's every move is being observed by a minstrel, the knight's activities are in no way hampered because of the minstrel's presence.

But Spring's AOP can be used for even more practical things than composing ageless sonnets about knights. As you'll see, AOP can be used to provide enterprise services such as declarative transactions and security.

1.5.3 AOP in the enterprise

Enterprise applications often require certain services such as security and transactional support. One way of applying these services is to code support for them directly into the classes that use them. For example, to handle transactions, you may place the following snippet throughout your code:

UserTransaction transaction = null;
try {
   transaction = ... {retrieve transaction}

   transaction.begin();

   ... do stuff...

   transaction.commit();
   } catch (Exception e) {
      if (transaction != null) transaction.rollback();
   }

The problem with handling transactions this way is that you may repeat the same transaction handling code several times—once for each time you need a transactional context. What's more, your application code is responsible for more than its core functionality.

EJB simplifies things by making it possible to declare these services and their policies in the EJB deployment descriptor. With EJB it is possible to write components that are ignorant of the fact that they are in a transactional context or being secured and then declare the transactional and security policies for those components in the EJB deployment descriptor. For example, to ensure that a method is transactional in EJB, you simply place the following in the deployment descriptor:

<container-transaction>
   <method>
      <ejb-name>Foo</ejb-name>
      <method-intf>Remote</method-inf>
      <method-name>doSomething</method-name>
   </method>
   <trans-attribute>RequiresNew</trans-attribute>
</container-transaction>

EJB has hung its hat on how it simplifies infrastructure logic such as transactions and security. But as we discussed in the introduction to this chapter, EJB has complicated matters in other ways.

Although Spring's AOP support can be used to separate cross-cutting concerns from your application's core logic, its primary job is as the basis for Spring's support for declarative transactions. Spring comes with several aspects that make it possible to declare transaction policies for JavaBeans. And the Acegi Security System (another open-source project associated with Spring) provides declarative security to JavaBeans. As with all Spring configuration, the transactional and security policies are prescribed in a Spring configuration file.

Note: Although the Spring framework comes packed with several frameworks and support for several enterprise-level services, it does not come with much to assist you with security. The Acegi security system uses Spring's AOP support as the foundation of a framework that adds declarative security to Spring-enabled applications. You will learn more about Acegi in chapter 11.

For example, suppose that instead of a knight your application handles student registration for training courses. Perhaps you have a bean called StudentServiceImpl that implements the following interface:

public StudentService {
   public void registerForCourse(Student student, Course course);
}

This bean may be registered in the Spring bean XML file as follows:

<bean id="studentServiceTarget"
      class="com.springinaction.training.StudentServiceImpl"/>

StudentService's registerForCourse() method should perform the following actions:

  1. Verify that there is an available seat in the course.
  2. Add the student to the course's roster.
  3. Decrement the course's available seat count by 1.
  4. Notify the student by e-mail of a successful registration.

All of these actions should happen atomically. If anything goes bad, then all should be rolled back as if nothing happened. Now imagine if instead of a minstrel providing musical logging to this class, you were to apply one of Spring's transaction manager aspects. Applying transactional support to StudentServiceImpl might be as simple as adding the lines shown in listing 1.13 to the bean XML file.

Listing 1.13 Declaring StudentService to be transactional



Click here for a larger image.

Here we make use of Spring's TransactionProxyFactoryBean. This is a convenience proxy class that allows us to intercept method calls to an existing class and apply a transaction context. In this case we are creating a proxy to our StudentServiceImpl class and applying a transaction to the registerForCourse() method. We are also using HibernateTransactionManager, the implementation of a transaction manager you would most likely use if your application's persistence layer is based on Hibernate.

Although this example leaves a lot to be explained, it should give you a glimpse of how Spring's AOP support can provide plain-vanilla JavaBeans with declarative services such as transactions and security. We'll dive into more details of Spring's declarative transaction support in chapter 5.

1.6 Spring alternatives

Whew! After that whirlwind introduction of Spring, you have a pretty good idea of what it can do. Now you are probably chomping at the bit to get down into the details so you can see how you can use Spring for your projects. But before we do that, we need to cover what else is out there in the world of J2EE frameworks.

1.6.1 Comparing Spring to EJB

Because Spring comes with rich support for enterprise-level services, it is positioned as a viable alternative to EJB. But EJB, as opposed to Spring, is a well-established platform. Therefore, the decision to choose one over the other is not one to be taken lightly. Also, you do not necessarily have to choose only Spring or EJB. Spring can be used to support existing EJBs as well, a topic that will be discussed in detail in chapter 7. With that in mind, it is important to know what these two have in common, what sets them apart, and the implications of choosing either.

EJB is a standard

Before we delve into the technical comparisons between Spring and EJB, there is an important distinction that we need to make. EJB is a specification defined by the JCP. Being a standard has some significant implications:

  • Wide industry support—There is a whole host of vendors that are supporting this technology, including industry heavyweights Sun, IBM, Oracle, and BEA. This means that EJB will be supported and actively developed for many years to come. This is comforting to many companies because they feel that by selecting EJB as their J2EE framework, they are going with a safe choice.
  • Wide adoption—EJB as a technology is deployed in thousands of companies around the world. As a result, EJB is in the tool bag of most J2EE developers. This means that if a developer knows EJB, they are more likely to find a job. At the same time, companies know that if they adopt EJB, there is an abundance of developers who are capable of developing their applications.
  • Toolability—The EJB specification is a fixed target, making it easy for vendors to produce tools to help developers create EJB applications more quickly and easily. Dozens of applications are out there that do just that, giving developers a wide range of EJB tool options.

Spring and EJB common ground

As J2EE containers, both Spring and EJB offer the developer powerful features for developing applications. Table 1.1 lists the major features of both frameworks and how the implementations compare.

Table 1.1 Spring and EJB feature comparison

Feature EJB Spring
Transaction management
  • Must use a JTA transaction manager.
  • Supports transactions that span remote method calls.
  • Supports multiple transaction environments through its PlatformTransactionManager interface, including JTA, Hibernate, JDO, and JDBC.
  • Does not natively support distributed transactions—it must be used with a JTA transaction manager.
Declarative transaction support
  • Can define transactions declaratively through the deployment descriptor.
  • Can define transaction behavior per method or per class by using the wildcard character *.
  • Cannot declaratively define rollback behavior—this must be done programmatically.
  • Can define transactions declaratively through the Spring configuration file or through class metadata.
  • Can define which methods to apply transaction behavior explicitly or by using regular expressions.
  • Can declaratively define rollback behavior per method and per exception type.
Persistence Supports programmatic bean-managed persistence and declarative container managed persistence. Provides a framework for integrating with several persistence technologies, including JDBC, Hibernate, JDO, and iBATIS.
Declarative security
  • Supports declarative security through users and roles. The management and implementation of users and roles is container specific.
  • Declarative security is configured in the deployment descriptor.
  • No security implementation out-of-the box.
  • Acegi, an open source security framework built on top of Spring, provides declarative security through the Spring configuration file or class metadata.
Distributed computing Provides container-managed remote method calls. Provides proxying for remote calls via RMI, JAX-RPC, and web services.

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 Name Description
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

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

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

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

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).

WebWork

WebWork is another MVC framework. Like Struts and Spring, it supports multiple view technologies. One of the biggest differentiators for WebWork is that it adds another layer of abstraction for handling web requests. The core interface for handling requests is the Action interface, which has one method: execute(). Notice that this interface is not tied to the web layer in any way. The WebWork designers went out of their way to make the Action interface unaware that it could be used in a web context. This is good or bad, depending on your perspective. Most of the time it will be used in a web application, so hiding this fact through abstraction does not buy you much.

A feature that WebWork provides that Spring does not (at least, not explicitly) is action chaining. This allows you to map a logical request to a series of Actions. This means you can create several Action objects that all perform discrete tasks and chain them together to execute a single web request.

Tapestry

Tapestry is another open source web framework that is quite different than ones mentioned previously. Tapestry does not provide a framework around the request-response servlet mechanism, like Struts or WebWork. Instead, it is a framework for creating web applications from reusable components (if you are familiar with Apple's WebObjects, Tapestry was inspired by its design).

The idea behind Tapestry is to relieve the developer from thinking about Session attributes and URLs, and instead think of web applications in terms of components and methods. Tapestry takes on the other responsibilities, such as managing user state and mapping URLs to methods and objects.

Tapestry provides a view mechanism as well. That is, Tapestry is not a framework for using JSPs—it is an alternative to JSPs. Much of Tapestry's power lies in its custom tags that are embedded with HTML documents and used by the Tapestry framework. Needless to say, Tapestry provides a unique web application framework. To learn more about Tapestry, take a look at Tapestry in Action (Manning, 2004).

1.6.4 Persistence frameworks

There really isn't a direct comparison between Spring and any persistence framework. As mentioned earlier, Spring does not contain any built-in persistence framework. Instead, Spring's developers recognized there were already several good frameworks for this and felt no need to reinvent the wheel. They created an ORM module that integrates these frameworks with rest of Spring. Spring provides integration points for Hibernate, JDO, OJB, and iBATIS.

Spring also provides a very rich framework for writing JDBC. JDBC requires a lot of boilerplate code (getting resources, executing statements, iterating though query results, exception handling, cleaning up resources). Spring's JDBC module handles this boilerplate, allowing you to focus on writing queries and handling the results.

Spring's JDBC and ORM frameworks work within Spring's transaction management framework. This means you can use declarative transactions with just about any persistence framework you choose.

1.7 Summary

You should now have a pretty good idea of what Spring brings to the table. Spring aims to make J2EE development easier, and central to this is its inversion of control. This enables you to develop enterprise applications using simple Java objects that collaborate with each other through interfaces. These beans will be wired together at runtime by the Spring container. It lets you maintain loosely coupled code with minimal cost.

On top of Spring's inversion control, Spring's container also offers AOP. This allows you place code that would otherwise be scattered throughout you application in one place—an aspect. When your beans are wired together, these aspects can be woven in at runtime, giving these beans new behavior.

Staying true to aiding enterprise development, Spring offers integration to several persistence technologies. Whether you persist data using JDBC, Hibernate, or JDO, Spring's DAO frameworks ease your development by providing a consistent model for error handling and resource management for each of these persistence frameworks.

Complementing the persistence integration is Spring's transaction support. Through AOP, you can add declarative transaction support to your application without EJB. Spring also supports a variety of transaction scenarios, including integration with JTA transactions for distributed transactions.

Filling out its support for the middle tier, Spring offers integration with other various J2EE services, such as mail, EJBs, web services, and JNDI. With its inversion of control, Spring can easily configure these services and provide your application objects with simpler interfaces.

To help with the presentation tier, Spring supports multiple view technologies. This includes web presentation technologies like Velocity and JSP as well as support for creating Microsoft Excel spreadsheets and Adobe Acrobat Portable Document Format (PDF) files. And on top of the presentation, Spring comes with a built-in MVC framework. This offers an alternative to other web frameworks like Struts and WebWork and more easily integrates with all of the Spring services.

End Notes

5 Think of minstrels as musically inclined logging systems of medieval times.

About the Authors

Craig Walls is a software developer with over 10 years' experience and co-author of XDoclet in Action. He has sucessfully implemented a number of Spring applications. Craig lives in Denton, Texas.

An avid supporter of open source Java technologies, Ryan Breidenbach has been developing Java web applications for the past five years. He lives in Coppell, Texas.

About the Book

Spring in Action by Craig Walls and Ryan Breidenbach

Published February 2005, Softbound, 472 pages
Published by Manning Publications Co.
ISBN 1932394354
Retail price: $44.95
Ebook price: $22.50. To purchase the ebook go to http://www.manning.com/walls2.
This material is from Chapter 1 of the book.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date