AOP made easy with AspectJ and Spring

I recently started looking at Aspect Oriented Programming (AOP) and I’m finding it exciting to say the least. Of course I was acquainted with it, since I saw it used for transaction management within Spring but I have never looked at it in depth. In this article I want to show how quick it is to get up to speed with AOP and Spring thanks to AspectJ. The material in this article is based on the excellent AOP book AspectJ in Action by Ramnivas Laddad.

AOP is not a language, but rather an approach to software engineering. Like any methodology it has got different implementations and AspectJ is currently the richest and most complete of all. Since AspectJ and AspectWerkz merged, it is now possible to create aspects using annotations.

The reason developers write code is to provide functionality of some sort. The kind of functioniality is not important for this discussion: some might want to deliver business functionality, others might write code for research purposes, other for sheer fun. The point is that any information system has got a core motive, a key functionality which it wants to deliver. For instance, I recently wrote PODAM, a testing tool which has as its ultimate goal that of automatically fill POJO / JavaBean properties.

Every information system has also got needs for orthogonal services (what AOP calls crosscutting concerns); for instance logging, security, auditing, exception management and so on. While an information system can be divided into discrete pieces of functionality (what AOP defines join points), orthogonal services are required across the board. For instance, if one wanted to log how long the execution of every single public method took, each public method should have something like the following pseudo-code:

public void someBusinessMethod() {

  long start = System.currentTimeInMilliseconds();

  doTheBusinessFunctionality();

  long end = System.currentTimeInMilliseconds();

  log.debug("The execution of someBusinessMethod took " + (end - start) + " milliseconds");

}

In the above method, the core functionality is identified solely by someBusinessMethod() whereas everything else is just logging activity. It would be nice to have something like:

//Some external magic happens before the invocation of this method to take the start time
public void someBusinessMethod() {

  doTheBusinessFunctionality(); 

}
//Some external magic happens after the invocation of this method to take the end time and logs how long the execution took.

Developers typically want logging, security, etc. throughout their application, not for a single method; AOP allows developers to achieve this goal by defining somewhere externally (called an Aspect) the behaviour to apply to all code matching some pattern (AOP actually allows for a broader set of functionalities, such as the possibility to add interfaces, instance variables, methods, etc to a class just to name one). This empowered behaviour is then somewhat added to the final executing code by what AOP calls a Weaver.

There are various ways that this can be achieved: weaving can happen at the source level, at the binary level and at load time. You could think of the weaver as the linker in C and C++; sources and libraries are linked together to create an executable; the weaver combines together Java code and aspects to create empowered behaviour.

Spring achieves this empowered behaviour by creating an AOP proxy around the code whose behaviour must be enriched. The code that follows shows a very simple example based on AspectJ; the example surrounds the execution of a simple method with some Authentication service.

The Authentication services looks very simple (the point is not how the functionality has been implemented but rather that an authentication service is available):

/**
 * 
 */
package uk.co.jemos.aop;

/**
 * A simple authenticator service.
 * 
 * @author mtedone
 * 
 */
public class Authenticator {

    public void authenticate() {
        System.out.println("Authenticated");
    }
    
}

Now let's have a look at the business logic:

/**
 * 
 */
package uk.co.jemos.aop;

/**
 * A simple service which delivers messages
 * @author mtedone
 * 
 */
public class MessageCommunicator {

    public void deliver(String message) {
        System.out.println(message);
    }

    public void deliver(String person, String message) {
        System.out.println(person + ", " + message);
    }

}

What we would like is for the Authenticator to be invoked before the invocation of any of the business methods of MessageCommunicator. Using AspectJ annotation syntax, we write in Aspect in pure Java:

package uk.co.jemos.aop;

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

@Aspect
public class SecurityAspect {    

    private Authenticator authenticator = new Authenticator();

    @Pointcut("execution(* uk.co.jemos.aop.MessageCommunicator.deliver(..))")
    public void secureAccess() {
    };

    @Before("secureAccess()")
    public void secure() {

        System.out.println("Checking and authenticating user...");
        authenticator.authenticate();

    }

}
  

The code above is a bit more interesting. An Aspect is marked with the @Aspect annotation. A Pointcut is some point of interest in our code, where we would like our Aspect to kick in. The syntax

@Pointcut(“execution(* uk.co.jemos.aop.MessageCommunicator.deliver(..))”) public void secureAccess() { };

means: “Define a Pointcut named secureAccess which applies to all deliver methods within the MessageCommunicator class, regardless of the return type of such method”. What follows is called an advice, and it’s where AOP empowers the behaviour of our class:

@Before("secureAccess()")
public void secure() {

   System.out.println("Checking and authenticating user...");
   authenticator.authenticate();

}

The code above says: “Before any match of the secureAccess() Pointcut apply the code within the block”. All of the above is pure Java, although the annotations belong to the AspectJ runtime. To use the above aspect with Spring, I defined a Spring context file:

<?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: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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <aop:aspectj-autoproxy />
    
    <bean id="messageCommunicator" />
    
    <bean id="securityAspect" />

</beans>

The XML element: <aop:aspectj-autoproxy /> instructs Spring to create a proxy around every aspect. Now when I use the MessageCommunicator from a client:

/**
* @param args
*/
public static void main(String[] args) {
 ApplicationContext ctx = new ClassPathXmlApplicationContext(
  "classpath:aop-appContext.xml");

 MessageCommunicator communicator = ctx.getBean("messageCommunicator",
  MessageCommunicator.class);
 communicator.deliver("Hello World");
 communicator.deliver("Marco", "Hello World");
}

I get the following output:

INFO: Loading XML bean definitions from class path resource [aop-appContext.xml] 15-May-2011 11:51:41
org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@21b64e6a: defining beans [org.springframework.aop.config.internalAutoProxyCreator,messageCommunicator,securityAspect];
root of factory hierarchy Checking and authenticating user… Authenticated Hello World
 Checking and authenticating user… Authenticated Marco, Hello World

AOP substantially changes the way we think software engineering, by allowing us to externalise crosscutting concerns in external components which are then weaved into our code when needed.This allows for cleaner and more maintainable code and the implementations are limitless. Additionally, if we are careful in writing our Aspects by making them reusable, we can quickly come up with a library of general-purpose, reusable aspects which add functionality to our code in an injected way.

There are obviously drawbacks in the adoption of AOP, mainly the learning curve which is required by developers to get acquainted with the technology. AspectJ defines its own language and syntax, as the example above demonstrates); the @Before annotation is just one possibility: advices can be applied before, after, around objects; additionally the syntax to define Pointcuts is not Java but rather script-like. AspectJ aspects also have keywords and native objects to capture the context of the join points they advice, and this syntax needs to be learned. However, the potential gains outweight by large the extra effort required in learning this new and exciting technology.

Reference: AOP made easy with AspectJ and Spring from our JCG partner Marco Tedone at the Marco Tedone’s blog.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


4 × = twelve



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books