BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Simplifying Enterprise Applications with Spring 2.0 and AspectJ

Simplifying Enterprise Applications with Spring 2.0 and AspectJ

This item in japanese

Bookmarks

Spring: Simple and Powerful

Spring aims to make enterprise application development as simple and productive as possible. Examples of this philosophy can be seen in Spring's approach to JDBC, ORM, JMX, dependency injection, and many other important areas of enterprise application development. Spring also distinguishes between making something simple, and making it simplistic. The elusive combination is to provide simplicity and power. One source of complexity in enterprise applications arises from the implementation of features and requirements that impact multiple parts of the application. Code relating to these features ends up scattered across the application code, making it harder to add, maintain, and understand. Spring 2.0 makes it much simpler to implement such features in a modular manner, greatly simplifying the overall application code and in some cases making it tractable to implement requirements that otherwise would just be too painful to code.

Transaction management is an example of a feature that impacts multiple parts of an application: typically all of the operations in the service layer. The way requirements such as this are addressed in Spring is through the use of AOP. Spring 2.0 offers a significant simplification in its support for AOP, while at the same time offering more expressive power than was available in Spring 1.x. The improvements come in two main areas: configuration is greatly simplified through the use of XML schema, and integration with AspectJ enables greater expressive power and a much simpler advice model.

In this article I will first describe where Spring AOP and AspectJ fit when in a typical enterprise application, then I'll show you the new Spring AOP support in 2.0. The bulk of the article is dedicated to walking through an adoption roadmap for AOP in enterprise applications, with plenty of examples of features that can be implemented simply using AOP, but would be very hard to do any other way.

Simplifying Enterprise Applications

A typical enterprise application - say a web app - is structured in a number of layers. A web layer with views and controllers, a service layer presenting the business interface of the system, a data-access or repository layer responsible for storing and retrieving persistent domain objects, and working alongside all of these, a domain model in which the core business logic resides.

The web layer, service layer, and data-access layer share a number of important characteristics: they should be as thin as possible, they should contain no business logic, and they are typically wired together by Spring. In these layers, Spring is responsible for object creation and configuration. The domain model is somewhat different: domain objects are created by the programmer using the new operator (or inflated by an ORM tool upon retrieval from the database). There are many unique instances of domain objects, and they (can) have rich behaviour.

It's ok for the service layer to contain application use-case specific logic, but all domain related logic should be in the domain model itself.

The service layer is typically the place where declarative enterprise services (such as transactions) are used. Declarative enterprise services such as transactions and security are great examples of requirements that impact many points in the application. In fact, even if you only wanted (say) transaction demarcation in a single place, it is still desirable to separate this function from your application logic to keep the code simpler and avoid unnecessary coupling.

Since service objects are Spring-managed beans, Spring AOP is a natural fit to address requirements in this layer. In fact, anyone using Spring's declarative transaction support is already using Spring AOP, whether they realise this or not. Spring AOP is mature and widely used. It's a great fit for Spring-managed beans in the web, service, and data-access layers so long as your requirements can be addressed by advising bean method execution (and many use cases for these layers fit into this category).

When it comes to requirements that impact multiple points in your domain model, the most important part of your application, Spring AOP is of much less assistance. You could use Spring AOP programmatically, but this would be very awkward and leave you with all the responsibility for proxy creation and identity management yourself. AspectJ is a natural fit for implementing features that impact domain objects. AspectJ aspects don't need any special proxy creation, and can happily advise objects created at runtime either in your application code or by frameworks you may be using. AspectJ is also a very good solution when you want to modularise behaviour that cuts across all the different layers of your application, or that is in any way performance sensitive.

What we'd ideally like therefore, is a consistent approach to Spring AOP and AspectJ so that we can easily use the two together, and so that skills you develop using (e.g.) Spring AOP can transfer across to AspectJ if your requirements grow. Whatever combination we're using, we'd still like all of the benefits of dependency injection and configuration that Spring offers. The new AOP support in Spring 2.0 delivers exactly this.

The underlying technology: A brief introduction to AspectJ and Spring AOP

AOP makes it much simpler to implement features that impact multiple points in an application. The primary reason for this is that AOP provides support for something known as advice. Unlike a method which has to be explicitly invoked, advice executes automatically whenever a matching trigger event occurs. Continuing on the transactions theme, the trigger event is the execution of a method in the service layer, and the advice logic provides the needed transaction demarcation. In AOP parlance, the trigger events are known as join points, and a pointcut expression is used to select the join points at which the advice runs. This simple inversion means that instead of having to scatter calls to the transaction manager all throughout your application code, you instead simply need to write a pointcut expression that defines all the points where you need the transaction manager to do something, and associate that with the appropriate advice. Both AspectJ and Spring AOP provide support for this model, and in fact they share the exact same pointcut expression language.

In the discussion that follows, it's important to note that Spring and AspectJ remain as separate projects. Spring simply uses the reflection and tools APIs exposed by AspectJ 5 as a library. Spring 2.0 is still a runtime proxy-based framework and the AspectJ weaver is not used for Spring aspects.

As I'm sure most of you are aware, AspectJ is a language with a full compiler (built as an extension of the Eclipse JDT Java compiler), and support for weaving (linking aspects with) binary class files either offline or at runtime as classes are loaded into the virtual machine. The most recent release of AspectJ is AspectJ 5, which provides full support for the Java 5 language.

AspectJ 5 also introduced a second style of aspect declaration, which we call "@AspectJ", that allows you to write an aspect as a regular Java class with annotations. Such an aspect can be compiled by a regular Java 5 compiler. For example, the traditional "HelloWorld" aspect in the AspectJ programming language looks like this:

public aspect HelloFromAspectJ {

  pointcut mainMethod() : execution(* main(..));

  after() returning : mainMethod() {
    System.out.println("Hello from AspectJ!);
  }

}

Compile this aspect alongside the traditional HelloWorld class and when you run the application you'll see the output:

Hello World!
Hello from AspectJ!

We can write the same aspect in the @Aspect style as follows:

@Aspect
public class HelloFromAspectJ {

  @Pointcut("execution(* main(..))")
  public void mainMethod() {}

  @AfterReturning("mainMethod()")
  public void sayHello() {
    System.out.println("Hello from AspectJ!");
  }

}

For the purposes of this article, the other important new features in AspectJ 5 are a full AspectJ-aware reflection API (you can ask an aspect at runtime for its advice and pointcut members, and so on), and a tools API that lets third-parties use AspectJ's pointcut parsing and matching engine. The first significant user of these APIs, as you will see shortly, is Spring AOP.

In contrast to AspectJ, Spring AOP is a proxy-based runtime framework. There are no special tool or build requirements when using Spring AOP, thus Spring AOP is a very easy way to get started. Being a proxy-based framework has both advantages and disadvantages. In addition to the ease of use factors already mentioned, a proxy-based framework is able to advise different instances of the same type independently. Contrast this to the type-based semantics of AspectJ where every instance of a type has the same behaviour. For a framework such as Spring, being able to advise individual objects (Spring beans) independently is an important requirement. On the downside, Spring AOP supports only a subset of AspectJ's capabilities: it is possible to advise the execution of methods on Spring beans, but nothing else.

Proxy-based frameworks typically suffer from issues of identity :- there are two objects (the proxy and the target) both representing the same entity in the application. Care must be taken to pass the appropriate reference at all times, and to ensure that proxies are created for any new target objects that are instantiated. Spring AOP neatly solves these issues by managing bean instantiation (so that proxies can be created transparently), and through dependency injection (so that Spring can always inject the appropriate reference).

New AOP support in Spring 2.0

Spring AOP in 2.0 is fully backwards compatible with Spring 1.x applications and configuration. It also offers simpler and more powerful configuration than Spring 1.x. The new AOP support is schema based so you'll need the relevant namespace and schema location attributes in your Spring beans configuration file. Here's how it looks:

<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">

 ...

</beans>

Compared to the simpler xml configuration needed when using a DTD, then so far we're not winning - but this is standard xml configuration and can be set up in a template in your IDE and simply reused each time you need to create a Spring configuration. You'll see the advantages as we start to add some content to the configuration...

Spring 2.0 uses the AspectJ pointcut language by default (restricted to execution join point kinds). If it sees an AspectJ pointcut expression, it calls out to AspectJ to parse it and match it. This means that any pointcut expression you write with Spring AOP will work in exactly the same way with AspectJ. Moreover, Spring can actually understand @AspectJ aspects, so it is possible to share entire aspect definitions between Spring and AspectJ. Enabling this capability is easy, just include the <aop:aspectj-autoproxy> element in your configuration. If Aspectj-autoproxying is enabled in this way, then any bean defined in your application context with a declared type that is an @AspectJ aspect will be interpreted as an aspect by Spring AOP and beans in the context will be advised accordingly.

Here's the Hello World program as it looks when using Spring AOP in this way. First, the content of the beans element in the application context file:

	<bean id="helloService"
	   class="org.aspectprogrammer.hello.spring.HelloService"/>

	<aop:aspectj-autoproxy/>

	<bean id="helloFromAspectJ"
	   class="org.aspectprogrammer.hello.aspectj.HelloFromAspectJ"/>

HelloService is a simple Java class:

public class HelloService {

  public void main() {
    System.out.println("Hello World!");
  }

}

HelloFromAspectJ is exactly the same annotated Java class (@AspectJ aspect) that you saw earlier in this article. Here's a tiny main class that fires up the Spring container, gets a reference to the helloService bean, and calls the 'main' method on it.

public class SpringBoot {

  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext(
      "org/aspectprogrammer/hello/spring/application-context.xml");
    HelloService service = (HelloService) context.getBean("helloService");
    service.main();
  }

}

Running this program produces the output:

Hello World!
Hello from AspectJ!

Remember, this is still Spring AOP (we're not using the AspectJ compiler or weaver at all), but it is AspectJ providing reflective information about the @AspectJ aspect, and parsing and matching the pointcuts on behalf of Spring.

Spring 2.0 also supports an xml form of aspect declaration backed by a simple POJO (no need for any annotations). The xml form also uses the same subset of the AspectJ pointcut language and supports the same five AspectJ advice types (before, after returning, after throwing, after [finally], and around advice).

Here's how the hello world application looks using an XML based aspect declaration:

   <bean id="helloService"
      class="org.aspectprogrammer.hello.spring.HelloService"/>

   <aop:config>
     <aop:aspect ref="helloFromSpringAOP">
	<aop:pointcut id="mainMethod" expression="execution(* main(..))"/>
	<aop:after-returning pointcut-ref="mainMethod" method="sayHello"/>
     </aop:aspect>
   </aop:config>

   <bean id="helloFromSpringAOP"
         class="org.aspectprogrammer.hello.spring.HelloAspect"/>

The elements in the aop namespace can be used to declare aspects, pointcuts, and advice with exactly the same semantics as their AspectJ and @AspectJ counterparts. An "aspect" element refers to a Spring bean (fully configured and instantiated by Spring), and each advice element specifies the method on that bean that will be invoked to execute the advice. In this case the HelloAspect class is simply:

public class HelloAspect {

  public void sayHello() {
    System.out.println("Hello from Spring AOP!");
  }

}

Running the program will produce the familiar output:

Hello World!
Hello from Spring AOP!

This would be a good point to download Spring 2.0 and try some of this out for yourself, if you haven't done so already.

Rather than turn this article into a full tutorial on Spring AOP, I want to press on look at some examples of features that can be usefully implemented this way. I will just point out in passing that one of things Spring gets from using the AspectJ pointcut language is the ability to write statically typed advice (methods that declare the parameters they actually need) as opposed to always working with untyped Object arrays - this makes advice methods much simpler to write.

Adoption roadmap

Enough of the theory... let's look at some examples of how and why you might actually use AOP in an enterprise application. Starting out with AOP does not have to be an all-or-nothing big-bang approach. Adoption can proceed in phases, each phase bringing increasing benefit in return for increased exposure to the technology.

The recommended adoption roadmap is to start out simply using the out-of-the-box aspects (such as transaction management) that Spring supplies. Many users of Spring will be doing this already, perhaps without appreciating that AOP is being used "under the covers". Following on from this, you can implement any custom crosscutting requirements you may have in the web, service, and data-access layers using Spring AOP.

Implementing features that impact the domain model necessitates the use of AspectJ. You may be surprised to learn that there are a number of AspectJ aspects that are useful to you at development time without affecting the application that runs in production in any way. These aspects can add a lot of value and have very little adoption risk, so they are the recommended way to get started with AspectJ. Following on from this, you may choose to implement 'infrastructural' requirements with AspectJ - typical examples would be profiling, tracing, error-handling and so on. As you grow more comfortable with AspectJ and the accompanying tools, you may finally start to implement functionality in the domain logic itself using aspects.

For additional information on the AOP adoption roadmap see chapter 11 of the book "Eclipse AspectJ", or Ron Bodkin's "Next steps with aspects" article in the developerWorks AOP@Work series. Both of these resources focus solely on AspectJ, whereas here I'm looking at using Spring and AspectJ together.

Let's take a look at each of these adoption stages in turn.

The very first thing it's useful to do when using AOP on a project is to define a set of pointcut expressions that describe the different modules or layers in your application. These pointcut expressions will be useful across all of the different stages of adoption, and defining them once will reduce duplication and improve code clarity. If we write these pointcuts using the @AspectJ notation, they can be compiled by any regular Java 5 compiler. It is also possible to write the same thing using the regular AspectJ language keywords, compile the source file with ajc and add the resulting .class file to the classpath. I'll use @AspectJ as that's the easier route of the two when starting out with Spring AOP. Many readers will be familiar with the "jpetstore" sample application that ships with Spring. I've refactored this application a little and added some aspects to it (which we'll discuss later in this article). Here is the beginning of the "SystemArchitecture" aspect that captures the main layers and modules in the pet store:

@Aspect
public class SystemArchitecture {

	/**
     *  we're in the pet store application if we're within any
     *  of the pet store packages
     */
	@Pointcut("within(org.springframework.samples.jpetstore..*)")
        public void inPetStore() {}

	// modules
	// ===========

	@Pointcut("within(org.springframework.samples.jpetstore.dao..*)")
        public void inDataAccessLayer() {}

	@Pointcut("within(org.springframework.samples.jpetstore.domain.*)")
        public void inDomainModel() {}

	@Pointcut("within(org.springframework.samples.jpetstore.service..*)")
        public void inServiceLayer() {}

	@Pointcut("within(org.springframework.samples.jpetstore.web..*)")
        public void inWebLayer() {}

	@Pointcut("within(org.springframework.samples.jpetstore.remote..*)")
        public void inRemotingLayer() {}

	@Pointcut("within(org.springframework.samples.jpetstore.validation..*)")
        public void inValidationModule() {}

	// module operations
	// ==================

	@Pointcut("execution(* org.springframework.samples.jpetstore.dao.*.*(..))")
        public void doaOperation() {}

	@Pointcut("execution(* org.springframework.samples.jpetstore.service.*.*(..))")
        public void businessService() {}

	@Pointcut("execution(public * org.springframework.samples.jpetstore.validation.*.*(..))")
	public void validation() {}

}

Now that we've got a vocabulary for talking about the application ("inServiceLayer", "businessOperation", and so on), let's do something useful with it.

Using out-of-the-box Spring aspects

An advisor is a Spring concept carried over from Spring 1.x that embodies a very minimal aspect with a single piece of advice and associated pointcut expression. For transaction demarcation, an advisor is all we need. A typical transaction requirement is that all operations in the service layer need to execute in a transaction (REQUIRED semantics) using the default isolation level of the underlying resource manager(s). In addition, some operations may be marked as "read-only" transactions - this knowledge can give significant performance improvements for such transactions. The jpetstore advisor declaration is as follows:

<!--
   all aspect and advisor declarations are gathered inside an
   aop:config element
-->
<aop:config>

  <aop:advisor
    pointcut="org.springframework.samples.jpetstore.SystemArchitecture.businessService()"
    advice-ref="txAdvice"/>

</aop:config>

This declaration simply says that when executing a "businessService" we need to run the advice referenced by "txAdvice". The "businessService" pointcut is defined in the org.springframework.samples.jpetstore.SystemArchitecture aspect that we looked at previously. It matches the execution of any operation defined in a service interface.

Because transaction advice itself can require considerable configuration, Spring provides the tx:advice element in the tx namespace to make this much simpler and clearer. This is what the definition of the "txAdvice" bean looks like for the jpetstore application:

<!--
  Transaction advice definition, based on method name patterns.
  Defaults to PROPAGATION_REQUIRED for all methods whose name starts with
  "insert" or "update", and to PROPAGATION_REQUIRED with read-only hint
  for all other methods.
-->
<tx:advice id="txAdvice">
  <tx:attributes>
    <tx:method name="insert*"/>
    <tx:method name="update*"/>
    <tx:method name="*" read-only="true"/>
  </tx:attributes>
</tx:advice>

There's an even simpler way to configure transactions that uses annotations. When using the @Transactional annotation, the only XML you need is:

<!--
   Tell Spring to apply transaction advice based on the presence of
   the @Transactional annotation on Spring bean types.
-->
<tx:annotation-driven/>

When using the annotation approach, the PetService implementation would be annotated as follows:

/*
 * all operations have TX_REQUIRED, default isolation level,
 * read-write transaction semantics by default
 */
@Transactional
public class PetStoreImpl implements PetStoreFacade, OrderService {

  ...

  /**
   * override defaults on a per-method basis
   */
  @Transactional(readOnly=true)
  public Account getAccount(String username) {
    return this.accountDao.getAccount(username);
  }

  ...

}

Simplifying the web, service, and data-access layers

Spring AOP can be used to simplify the web, service, and data-access layers. In this section, we'll look at a couple of examples: one drawn from the data access layer, and one from the service layer.

Suppose you've implemented your data access layer using Hibernate 3, without using the Spring HibernateTemplate support classes. You now plan to start using Spring in the application, and would like to take advantage of Spring's fine-grained DataAccessException hierarchy in the service layer. Spring's HibernateTemplate will convert HibernateExceptions to DataAccessExceptions for you automatically, but since at this stage you have an existing data layer implementation you are quite happy with, you don't want to rewrite it based around the Spring support classes right now. That means you need to implement the exception translation yourself. The requirement is simple to state:

After any HibernateException is thrown from the data access layer, convert it to a DataAccessException before passing it on to the caller.

Using AOP, the implementation is almost as simple as that requirements statement. Implementing this requirement without AOP would be a major headache. This is what the HibernateExceptionTranslator aspect for "myapp" looks like:

@Aspect
public class HibernateExceptionTranslator {

  private HibernateTemplate hibernateTemplate;

  public void setHibernateTemplate(HibernateTemplate aTemplate) {
    this.hibernateTemplate = aTemplate;
  }

  @AfterThrowing(
    throwing="hibernateEx",
    pointcut="org.aspectprogrammer.myapp.SystemArchitecture.dataAccessOperation()"
  )
  public void rethrowAsDataAccessException(HibernateException hibernateEx) {
    throw this.hibernateTemplate
	      .convertHibernateAccessException(hibernateEx);

  }

}

The aspect needs a HibernateTemplate in order to perform the translation - we'll configure it using dependency injection just like any other Spring bean. The advice declaration should hopefully be very easy to understand as a direct translation of the requirements statement: "@AfterThrowing a HibernateException (hibernateEx) from a dataAccessOperation(), rethrowAsDataAccessException". Simple and powerful!

We could build the application with ajc (the AspectJ compiler) now, and we'd be done. But there's no need to use ajc here, because Spring AOP can also understand @AspectJ aspects...

In the application context file, we have two pieces of configuration needed. First we need to tell Spring that any bean with a type that is an @AspectJ aspect should be used to configure Spring AOP proxying. This is a one-off configuration achieved by declaring the following element anywhere in the application context configuration file:

<aop:aspectj-autoproxy>

Then we need to declare the exception translation bean and configure it, just like we would for any regular Spring bean (there is nothing AOP specific here):

<bean id="hibernateExceptionTranslator"
   class="org.aspectprogrammer.myapp.dao.hibernate.HibernateExceptionTranslator">
  <property name="hibernateTemplate">
    <bean class="org.springframework.orm.hibernate3.HibernateTemplate">
      <constructor-arg index="0" ref="sessionFactory" />
    </bean>
  </property>
</bean>

The mere fact that the class of the bean (HibernateExcepionTranslator) is an @AspectJ aspect is enough to configure Spring AOP.

For completeness, let's also look at how you could do this using the xml form of aspect declaration (for working under JDK 1.4 for example). The bean definition for the hibernateExceptionTranslator would be unchanged from that shown above. The class itself would no longer be annotated, but the rest of it is exactly the same too:

public class HibernateExceptionTranslator {

  private HibernateTemplate hibernateTemplate;

  public void setHibernateTemplate(HibernateTemplate aTemplate) {
    this.hibernateTemplate = aTemplate;
  }

  public void rethrowAsDataAccessException(HibernateException hibernateEx) {
    throw this.hibernateTemplate
	      .convertHibernateAccessException(hibernateEx);

  }

}

Since this is no longer an @AspectJ aspect, we can't use the aspectj-autoproxy element. Instead we define the aspect in XML:

<aop:config>

  <aop:aspect ref="hibernateExceptionTranslator">
     <aop:after-throwing
         throwing="hibernateEx"
         pointcut="org.aspectprogrammer.myapp.SystemArchitecture.dataAccessOperation()"
         method="rethrowAsDataAccessException"/>
  </aop:aspect>

</aop:config>

This reads just like the previous version: after-throwing a hibernateEx from a dataAccessOperation, rethrowAsDataAccessException. Notice the "ref" attribute of the aop:aspect element which refers to the hibernateExceptionTranslator bean we defined earlier. This is the bean instance on which the rethrowAsDataAccessException method will be invoked, and hibernateEx is the name of a parameter (the only parameter in this case) declared on that method.

So that's it. We've implemented the requirement (twice!). Using the @AspectJ style, we have 15 non-blank lines of code, and 1 line of XML. This is enough to give us consistent and correct behavior across the whole of the data access layer, however big it may be.

One nice advantage of this particular aspect is that if you later want to migrate the data layer to a JPA (EJB 3 persistence) based implementation using Hibernate or any other JPA implementation, your service layer will be unaffected and can carry on working with DataAccessExceptions (Spring will provide templates and exception translation for JPA, just as it does for other ORM implementations).

Now that we can work with fine-grained DataAccessExceptions in the service layer, we can do something useful with that. In particular, we could catch ConcurrencyFailureExceptions and transparently retry if an idempotent operation fails with, for example, a deadlock loser exception. Let's implement the crosscutting requirement that any idempotent service operation that fails due to a concurrency failure will be transparently retried a configurable number of times, before passing the failure onto the client.

Here's an aspect that does the job:

@Aspect
public class ConcurrentOperationExecutor implements Ordered {

  private static final int DEFAULT_MAX_RETRIES = 2;
  private int maxRetries = DEFAULT_MAX_RETRIES;
  private int order = 1;
  private boolean retryOnOptimisticLockFailure = false;

  /**
   * configurable number of retries
   */
  public void setMaxRetries(int maxRetries) {
    this.maxRetries = maxRetries;
  }

  /**
   * Whether or not optimistic lock failures should also be retried.
   * Default is not to retry transactions that fail due to optimistic
   * locking in case we overwrite another user's work.
   */
  public void setRetryOnOptimisticLockFailure(boolean retry) {
     this.retryOnOptimisticLockFailure = retry;
  }

  /**
   *  implementing the Ordered interface enables us to specify when
   *  this aspect should run with respect to other aspects such as
   *  transaction management. We give it the highest precedence
   *  (1) which means that the retry logic will wrap the transaction
   *  logic - we need a fresh transaction each time.
   */
  public int getOrder() {
    return this.order;
  }

  public void setOrder(int order) {
    this.order = order;
  }

  /**
   * For now, just assume that all business services are idempotent
   */
  @Pointcut("org.aspectprogrammer.myapp.SystemArchitecture.businessService()")
  public void idempotentOperation() {}

  @Around("idempotentOperation()")
  public Object doConcurrentOperation(ProceedingJoinPoint pjp)
  throws Throwable {
    int numAttempts = 0;
    ConcurrencyFailureException failureException;
    do {
      try {
	    return pjp.proceed();
      }
      catch(OptimisticLockingFailureException ex) {
        if (!this.retryOnOptimisticLockFailure) {
          throw ex;
        }
        else {
	     failureException = ex;
	    }
      }
      catch(ConcurrencyFailureException ex) {
         failureException = ex;
      }
    }
    while(numAttempts++ < this.maxRetries);
      throw lockFailureException;
    }

}

Once more, this aspect could be used by either Spring AOP or AspectJ, unchanged. The around advice (doConcurrentOperation) takes a special parameter of type ProceedingJoinPoint. When proceed is called on this object, whatever the advice is "around" (in this case the service operation) will execute.

If you strip out all of the comments and the boilerplate getters-and-setters the business end of this aspect is still only 32 lines of code. Since we already had the aspectj-autoproxy element in the configuration file, all we need to add is a simple bean definition:

<bean id="concurrentOperationExecutor"
     class="org.aspectprogrammer.myapp.service.impl.ConcurrentOperationExecutor">
  <property name="maxRetries" value="3"/>
  <property name="order" value="1"/>
</bean>

What if not all of the operations in the service layer are idempotent? How can we identify the idempotent ones? This is where the power of a pointcut language comes into play. We already have an abstraction that represents the concept of an idempotent operation:

  @Pointcut("org.aspectprogrammer.myapp.SystemArchitecture.businessService()")
  public void idempotentOperation() {}

If we want to change what constitutes what represents an idempotent operation, all we have to do is change the pointcut. For example, we might define a marker annotation for idempotent operations, @Idempotent. We could change the pointcut expression to only match business services with the Idempotent annotation very simply:

  @Pointcut(
    "org.aspectprogrammer.myapp.SystemArchitecture.businessService() &&
     @annotation(org.aspectprogrammer.myapp.Idempotent)")
  public void idempotentOperation() {}

Now that's a bit simpler than using APT! The pointcut simply says: "an idempotentOperation is businessService that has the Idempotent annotation.

Hopefully most of your service operations are idempotent. In that case, it might be easier to annotate the ones that are not idempotent than pick out the ones that are. Something like @IrreversibleSideEffects should do the trick. This works both technically and psychologically (who wants to annotate their code with "IrreversibleSideEffects"! I'd rather rewrite it to avoid them ;) ). Since the definition of idempotentOperation is captured in just one place, it's easy to change:

  @Pointcut(
    "org.aspectprogrammer.myapp.SystemArchitecture.businessService() &&
     !@annotation(org.aspectprogrammer.myapp.IrreversibleSideEffects)")
  public void idempotentOperation() {}

An idempotentOperation is a businessService that does not have the IrreversibleSideEffects annotation.

Improving productivity with development time aspects

Once you are comfortable writing @AspectJ aspects for Spring AOP, you can get a lot of extra benefit from AspectJ even if you only use it during development (and have no AspectJ compiled aspects in your running application). Aspects can be used to aid in testing (they make some kinds of mocking and fault injection easier), in debugging and diagnosing problems, and in ensuring that the design guidelines set out for your application are enforced.

First off, let's look at an example of a design enforcement aspect. Continuing the theme of working in the data access layer, we'd now like to introduce the Spring HibernateTemplate and let Spring manage Hibernate sessions for us instead of doing this ourselves. The following aspect will ensure that programmers don't inadvertantly start managing their own sessions:

public aspect SpringHibernateUsageGuidelines {

  pointcut sessionCreation()
    : call(* SessionFactory.openSession(..));

  pointcut sessionOrFactoryClose()
    : call(* SessionFactory.close(..)) ||
      call(* Session.close(..));

  declare error
   : sessionCreation() || sessionOrFactoryClose()
   : "Spring manages Hibernate sessions for you, " +
     "do not try to do it programmatically";
}

With this aspect in place, if a programmer is using the AspectJ Development Tools (AJDT) plugin for Eclipse, he or she will get a compilation error marker in the problems view and at the offending location in the source code (exactly the same as any regular compilation error) with the error text "Spring manages Hibernate sessions for you, do not try to do it programmatically". A recommended way to introduce enforcement aspects such as this is to add an AspectJ compilation step to the build process that "weaves" your application with the enforcement aspects - this task will fail if there are build errors found by the aspects.

Now let's look at a simple diagnosis aspect. Recall that we marked some transactions as read-only (an important performance optimisation). As applications grow in complexity, it can be a long way (conceptually) from the service layer operation where transaction demarcation takes place, to the business domain logic that executes as part of a given use case. If the domain logic updates the state of a domain object during a read-only transaction, we run the risk of that update being lost (never committed to the database). This can be a source of subtle bugs.

The LostUpdateDetector aspect can be used at development time as an aid to detecting potential lost updates.

public aspect LostUpdateDetector {

  private Log log = LogFactory.getLog(LostUpdateDetector.class);

  pointcut readOnlyTransaction(Transactional txAnn) :
    SystemArchitecture.businessService() &&
    @annotation(txAnn) && if(txAnn.readOnly());

  pointcut domainObjectStateChange() :
     set(!transient * *) &&
     SystemArchitecture.inDomainModel();

  ..

I've started out by defining a couple of useful pointcuts in the aspect. A readOnlyTransaction is the execution of a businessService() that has an @Transactional annotation with the readOnly() property set to true. A domainObjectStateChange is the update of any non-transient field inDomainModel(). (Note, this is a simplified, but still useful, perspective on what constitutes a domain object state change - we could expand the aspect to deal with collections etc. if we so desired). With these two concepts defined, we can now say what we mean by a potentialLostUpdate():

  pointcut potentialLostUpdate() :
    domainObjectStateChange() &&
    cflow(readOnlyTransaction(Transactional));

A potentialLostUpdate is a domainObjectState change made in the control flow of (during) a readOnlyTransaction. Here you can see the power of the pointcut language at work. We've been able to express a powerful concept very simply by composing two named pointcut expressions. Using a pointcut language it is much simpler to express conditions such as a potentialLostUpdate than it would be if you only had a raw interception model available to you. It's also a lot more powerful than simplistic interception mechanisms such as those offered by EJB 3.

Finally of course we need to actually do something when a potentialLostUpdate occurs:

  after() returning : potentialLostUpdate() {
    logLostUpdate(thisJoinPoint);
  }

  private void logLostUpdate(JoinPoint jp) {
    String fieldName = jp.getSignature().getName();
    String domainType = jp.getSignature().getDeclaringTypeName();
    String newValue = jp.getArgs()[0].toString();
    Throwable t = new Throwable("potential lost update");
    t.fillInStackTrace();
    log.warn("Field [" +  fieldName + "] in type [" + domainType + "] " +
	     "was updated to value [" + newValue + "] in a read-only " +
	     "transaction, update will be lost.",t);
  }

}

Here's the log entry resulting from running a test case with this aspect in place:

WARN - LostUpdateDetector.logLostUpdate(41) | Field [name] in type
[org.aspectprogrammer.myapp.domain.Pet] was updated to value [Mr.D.]
in a read-only transaction, update will be lost.
java.lang.Throwable: potential lost update
	at org.aspectprogrammer.myapp.debug.LostUpdateDetector.logLostUpdate(LostUpdateDetector.aj:40)
	at org.aspectprogrammer.myapp.debug.LostUpdateDetector.afterReturning(LostUpdateDetector.aj:32)
	at org.aspectprogrammer.myapp.domain.Pet.setName(Pet.java:32)
	at org.aspectprogrammer.myapp.service.impl.PetServiceImpl.updateName(PetServiceImpl.java:40)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:100)
	at org.aspectprogrammer.myapp.service.impl.ConcurrentOperationExecutor.doConcurrentOperation(ConcurrentOperationExecutor.java:37)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

Note as an aside the clean and readable stack trace (and the optimistic retry logic in place). The readable stack trace is due to another aspect that removes noise from exception stack trace entries. Without the stack trace management aspect in place, all of the Spring AOP interception stack frames are also displayed, giving a stack trace like that shown below. I think you'll agree that the simplified version is a big improvement!

WARN - LostUpdateDetector.logLostUpdate(41) | Field [name] in type
[org.aspectprogrammer.myapp.domain.Pet] was updated to value [Mr.D.]
in a read-only transaction, update will be lost.
java.lang.Throwable: potential lost update
	at org.aspectprogrammer.myapp.debug.LostUpdateDetector.logLostUpdate(LostUpdateDetector.aj:40)
	at org.aspectprogrammer.myapp.debug.LostUpdateDetector.ajc$afterReturning$org_aspectprogrammer_myapp_debug_LostUpdateDetector$1$b5d4ce0c(LostUpdateDetector.aj:32)
	at org.aspectprogrammer.myapp.domain.Pet.setName(Pet.java:32)
	at org.aspectprogrammer.myapp.service.impl.PetServiceImpl.updateName(PetServiceImpl.java:40)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:287)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:181)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:148)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:100)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:71)
	at org.aspectprogrammer.myapp.service.impl.ConcurrentOperationExecutor.doConcurrentOperation(ConcurrentOperationExecutor.java:37)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:568)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:558)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:57)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
	at $Proxy8.updateName(Unknown Source)
	at org.aspectprogrammer.myapp.debug.LostUpdateDetectorTests.testLostUpdateInReadOnly(LostUpdateDetectorTests.java:23)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at junit.framework.TestCase.runTest(TestCase.java:154)
	at junit.framework.TestCase.runBare(TestCase.java:127)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:118)
	at junit.framework.TestSuite.runTest(TestSuite.java:208)
	at junit.framework.TestSuite.run(TestSuite.java:203)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

Simplifying the implementation of 'infrastructural' requirements

As you start to get more comfortable with AspectJ and the accompanying tool set, you can use AspectJ to implement requirements that impact all parts of your application, including the domain model. As a simple example, I'll show you how to profile the jpetstore sample application. Let's look at the Profiler aspect first, and then fill in some of the surrounding details:

public aspect Profiler {

  private ProfilingStrategy profiler = new NoProfilingStrategy();

  public void setProfilingStrategy(ProfilingStrategy p) {
    this.profiler = p;
  }

  pointcut profiledOperation() :
    Pointcuts.anyPublicOperation() &&
    SystemArchitecture.inPetStore() &&
    !within(ProfilingStrategy+);

  Object around() : profiledOperation() {
    Object token = this.profiler.start(thisJoinPointStaticPart);
    Object ret = proceed();
    this.profiler.stop(token,thisJoinPointStaticPart);
    return ret;
  }
}

We've defined a profiledOperation() as anyPublicOperation() in[the]PetStore(). The aspect is acting as a controller delegating to a ProfilingStrategy, which we'll configure with Spring using dependency injection.

  <bean id="profiler"
     class="org.springframework.samples.jpetstore.profiling.Profiler"
     factory-method="aspectOf">
    <property name="profilingStrategy">
      <ref local="jamonProfilingStrategy"/>
    </property>
  </bean>

  <bean id="jamonProfilingStrategy"
     class="org.springframework.samples.jpetstore.profiling.JamonProfilingStrategy"
     init-method="reset"
     destroy-method="report">
  </bean>

Note the use of the "factory-method" attribute for the aspect bean, this is the only difference between configuring a singleton AspectJ aspect and configuring a regular Spring bean. I'm using JAMon for profiling which provides a very simple API.

public class JamonProfilingStrategy implements ProfilingStrategy {

  public Object start(StaticPart jpStaticPart) {
    return MonitorFactory.start(jpStaticPart.toShortString());
  }

  public void stop(Object token, StaticPart jpStaticPart) {
    if (token instanceof Monitor) {
      Monitor mon = (Monitor) token;
      mon.stop();
    }
  }
}

That's all we have to do to enable profiling across the entire pet store. By adding the JAMon supplied jsp to the pet store application, we can view the profiling output in a web browser. Here's a screenshot after I've clicked around the application for a while:

Simplifying the domain model

It's not uncommon to have business logic requirements that impact multiple parts of your domain model too. Some obvious examples would be design pattern implementations (see Nick Leseicki's excellent developerWorks articles on this topic: part 1, part 2), dependency injection of domain objects (using for example Spring's @Configurable annotation), and the implementation of business rules and policies. At this stage of adoption, your core business logic becomes dependent on the presence of aspects. Aspects you write will be particular to your domain. Both AspectJ and AJDT are built using AspectJ, and we use a number of domain-specific aspects in their construction. As an example, here's an aspect that I added to AspectJ during the development of the 1.5.1 release: it implements an often-requested feature to issue a lint warning if an exception is swallowed in an empty catch block.

public aspect WarnOnSwallowedException {

  pointcut resolvingATryStatement(TryStatement tryStatement, BlockScope inScope)
    : execution(* TryStatement.resolve(..)) &&
      this(tryStatement) &&
      args(inScope,..);

    after(TryStatement tryStatement, BlockScope inScope) returning
      : resolvingATryStatement(tryStatement,inScope) {
      if (tryStatement.catchBlocks != null) {
        for (int i = 0; i < tryStatement.catchBlocks.length; i++) {
	  Block catchBlock = tryStatement.catchBlocks[i];
	  if (catchBlock.isEmptyBlock() ||
              catchBlock.statements.length == 0) {
	    warnOnEmptyCatchBlock(catchBlock,inScope);
	  }
        }
      }
    }

    private void warnOnEmptyCatchBlock(Block catchBlock, BlockScope inScope) {
      inScope.problemReporter()
             .swallowedException(catchBlock.sourceStart(),
                                 catchBlock.sourceEnd());
    }
}

Even though this aspect only advises a single place in the codebase in this example, it makes the code clearer by modularising this AspectJ addition to the JDT compiler's functionality and making it very obvious for future maintainers how this feature is implemented.

A more detailed treatment of domain modelling with aspects is the subject of another article.

Summary

Spring aims to provide a simple and powerful approach to the development of enterprise applications. With it's support for AOP and integration with AspectJ, this approach extends to the implementation of features that impact multiple parts of an application. Traditionally the implementation of such features is scattered throughout the application logic, making it hard to add, remove and maintain the feature, and complicating application logic. Using aspects Spring enables you to write clean, simple, modular implements of such features.

Adoption of AOP can proceed in a number of phases: start out by taking advantage of aspects that Spring supplies out-of-the-box, and then you can add your own @AspectJ aspects using Spring AOP in the web, service, and data-access layers. AspectJ itself can be used to improve development productivity without introducing any runtime dependency on AspectJ. Going further, infrastructural requirements that cut across multiple layers of your application can be simply implemented using AspectJ aspects. Finally, you can use aspects to simplify the implementation of your domain model itself.

About the author

Adrian Colyer is Chief Scientist at Interface21, leader of the AspectJ project on Eclipse.org, and founder of the AspectJ Development Tools (AJDT) project. In 2004 he was voted one of the top 100 young innovators in the world by MIT Technology Review, and is a frequent speaker on the topics of Spring, AOP, and AspectJ.

About Interface21

Interface21 provide training and consultancy in Spring, AOP, and AspectJ. For course schedules or to arrange an in-house training see www.interface21.com.

See Adrian Colyer and the rest of the Spring community at http://www.thespringexperience.com conference, Dec 7-10, 2006.

Rate this Article

Adoption
Style

BT