Spring AOP example

05 December 2013
By Gonçalo Marques
In this tutorial we will see how to use Spring AOP through supported AspectJ annotations.

Introduction

One of the main features of the Spring Framework is the Aspect Oriented Programming (AOP) paradigm. It's main purpose is to aggregate sections of code that would be usually repeated in multiple places of your application in a single spot, for example boilerplate code to open-commit-rollback a transaction.

By removing boilerplate code from your business methods you are making them cleaner. Another example of such common use case for AOP is to log - or audit - calls to some (or all) business methods.

In this article we will implement a simple logging aspect that will intercept calls to a Spring service. Although Spring supports aspect configuration via XML we will configure our aspect using Spring supported AspectJ annotations.

This tutorial considers the following environment:

  1. Ubuntu 12.04
  2. JDK 1.7.0.21
  3. Spring 3.2.5
  4. AspectJ 1.7.4

The following Maven dependencies are required:

Required Maven dependencies

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <spring.version>3.2.5.RELEASE</spring.version>
  <aspectj.version>1.7.4</aspectj.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${aspectj.version}</version>
  </dependency>

  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${aspectj.version}</version>
  </dependency>

</dependencies>

The Spring service

Following next is a very simple Spring service that will be used in this example:

Example Spring service

package com.byteslounge.spring.service;

import org.springframework.stereotype.Service;

@Service
public class ExampleService {

  public void simpleMethod() {
    System.out.println("Inside simpleMethod");
  }

  public Object methodReturnsValue() {
    System.out.println("Inside methodReturnsValue");
    return new String("Hello from methodReturnsValue");
  }

  public void methodThrowsException() {
    System.out.println("Inside methodThrowsException");
    throw new RuntimeException("Exception from methodThrowsException");
  }

  public Object testAroundReturningResult() {
    System.out.println("Inside testAroundReturningResult");
    return new String("Hello from aroundReturningResult");
  }

  public void testAroundThrowingException() throws Exception {
    System.out.println("Inside testAroundThrowingException");
    throw new RuntimeException("Exception from testAroundThrowingException");
  }

}

Before AOP advice

The first Spring supported AOP advice we will see is the Before advice. As the name states this AOP advice will be called before intercepted methods are executed.

Before advice

package com.byteslounge.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingAspect {

  @Before(
  "execution(* com.byteslounge.spring.service.ExampleService.simpleMethod(..))"
  )
  public void beforeExecution(JoinPoint jp) {
    System.out.println("Before method: " + jp.getSignature().getName()
        + ". Class: " + jp.getTarget().getClass().getSimpleName());
  }

}

By annotating a class with @Aspect we are defining that class as an Aspect. An Aspect usually groups related advices. In our example our aspect will define all advices related with logging.

The method we just defined inside our aspect - beforeExecution() - is annotated with @Before. This annotation contains a pointcut that determines the advice to be executed before simpleMethod from the Spring service we defined earlier gets called.

The JoinPoint parameter passed into the advice contains all the information about the Service method being intercepted.

If we happen to hold a reference to our service and call simpleMethod:

exampleBean.simpleMethod();

The following output will be generated:

Before method: simpleMethod. Class: ExampleService
Inside simpleMethod

AfterReturning AOP advice

AfterReturning advice will be called if the method being intercepted returns a value without throwing an exception.

AfterReturning advice

package com.byteslounge.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LoggingAspect {

  @AfterReturning(
   pointcut = "execution(* com.byteslounge.spring.service.ExampleService.methodReturnsValue(..))",
   returning = "result"
  )
  public void afterReturningExecution(JoinPoint jp, Object result) {
    System.out.println("After returning method: "
      + jp.getSignature().getName() + ". Class: "
      + jp.getTarget().getClass().getSimpleName());
      System.out.println("Result returned: " + result);
  }

}

A new configuration parameter introduced in this advice pointcut is the returning value defined in @AfterReturning annotation.

This parameter value must have an exact match with the name of an argument of the advice method. The argument passed into the advice will be the return value of the intercepted Service method. Keep in mind that the advice method argument and the result of the intercepted service method must be of the same type (in this example the type is Object).

If we happen to hold a reference to our service and call methodReturnsValue:

Object result = exampleBean.methodReturnsValue();

The following output will be generated:

Inside methodReturnsValue
After returning method: methodReturnsValue. Class: ExampleService
Result returned: Hello from methodReturnsValue

AfterThrowing AOP advice

AfterThrowing advice will be called if the method being intercepted throws an exception.

AfterThrowing advice

package com.byteslounge.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LoggingAspect {

  @AfterThrowing(
   pointcut = "execution(* com.byteslounge.spring.service.ExampleService.methodThrowsException(..))",
   throwing = "ex"
  )
  public void afterThrowingExecution(JoinPoint jp, Exception ex) {
    System.out.println("After throwing method: "
        + jp.getSignature().getName() + ". Class: "
        + jp.getTarget().getClass().getSimpleName());
    System.out.println("Exception: " + ex.getMessage());
  }

}

Once again we have a new parameter in the advice pointcut: The throwing parameter.

This parameter value must have an exact match with the name of an argument of the advice method. The argument passed into the advice will be the exception thrown by the intercepted Service method.

If we happen to hold a reference to our service and call methodThrowsException:

exampleBean.methodThrowsException();

The following output will be generated:

Inside methodThrowsException
After throwing method: methodThrowsException. Class: ExampleService
Exception: Exception from methodThrowsException

After AOP advice

After advice will be called when the method being intercepted exits either returning a value or throwing an exception. Because of this behaviour this advice is also known as finally (analogy with the finally statement in a try-catch-finally block).

This kind of advice is useful for releasing or clean up previously acquired resources.

After advice

package com.byteslounge.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LoggingAspect {

  @After(
  "execution(* com.byteslounge.spring.service.ExampleService.simpleMethod(..))"
  )
  public void afterExecution(JoinPoint jp) {
    System.out.println("After method: " + jp.getSignature().getName()
        + ". Class: " + jp.getTarget().getClass().getSimpleName());
  }

}

If we happen to hold a reference to our service and call simpleMethod:

exampleBean.simpleMethod();

The following output will be generated:

Inside simpleMethod
After method: simpleMethod. Class: ExampleService

Around AOP advice

Around advice is the most powerful of all spring AspectJ supported advices.

This advice resembles an EJB interceptor. You have the possibility to execute code both before and after the intercepted method is called and even return a result to the caller instead of executing the intercepted method.

Suppose your intercepted service method would fetch a result from the database. You could check if the result to be fetched is stored in some kind of cache and return the cached value instead of executing the intercepted service method.

You may also catch unexpected exceptions from the intercepted method and wrap them in an exception that the caller is prepared to handle.

Around advice

package com.byteslounge.spring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LoggingAspect {

  @Around(
  "execution(* com.byteslounge.spring.service.ExampleService.testAround*(..))"
  )
  public Object aroundExecution(ProceedingJoinPoint jp) throws Exception {

    System.out.println("Before method: " + jp.getSignature().getName()
        + ". Class: " + jp.getTarget().getClass().getSimpleName());

    try {
      // Proceed with method invocation
      Object result = jp.proceed();

      System.out.println("Returning: " + result);
      return result;
    } catch (Throwable e) {
      // Log error
      System.out.println("Error: " + e.getMessage());
      // Throw exception to the caller
      throw new Exception("Error", e);
    }

  }

}

If we happen to hold a reference to our service and call testAroundReturningResult:

Object result = exampleBean.testAroundReturningResult();

The following output will be generated:

Before method: testAroundReturningResult. Class: ExampleService
Inside testAroundReturningResult
Returning: Hello from aroundReturningResult

If we call testAroundThrowingException instead:

exampleBean.testAroundThrowingException();

The following output will be generated:

Before method: testAroundThrowingException. Class: ExampleService
Inside testAroundThrowingException
Error: Exception from testAroundThrowingException

Spring configuration file

Finally the Spring configuration file used in this example:

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop.xsd">
    
  <context:component-scan base-package="com.byteslounge.spring" />
    
  <aop:aspectj-autoproxy />
   
  <bean id="loggingAspect" class="com.byteslounge.spring.aop.LoggingAspect">
  </bean>

</beans>

We are using the usual package component scan because the Spring service used in this example is configured using @Service annotation and is inside package com.byteslounge.spring.

aspectj-autoproxy element is required for enabling Spring AOP support.

Bean loggingAspect is defining our Aspect as a Spring managed bean. Since our Aspect class is also inside package com.byteslounge.spring we could have omitted this bean XML definition and rely on package scanning.

To do this we would have to include another Spring annotation in our Aspect class since the @Aspect annotation itself is not enough for Spring component scan detection (use for example @Component Spring annotation):

Aspect ready for package component scan

package com.byteslounge.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {

}

A fully working sample covering all the described scenarios is available for download at the bottom of this page.

Download source code from this article

Download link: spring-aop-example.zip

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: