Aspect Oriented Programming Notes #2: Setting Up AOP Dependencies and Declaring Aspects, Advices, Pointcuts

An aspect is a Java class that modularizes a set of concerns (e.g., logging or transaction management) that cuts across multiple types and objects. Java classes that modularize such concerns are decorated with the @Aspect annotation. In AOP terminology, aspects are also complemented by advices, which in themselves have pointcuts.

An advice is a simple Java method with one of the advice annotations. AspectJ supports five types of advice annotations:

@Before, @After, @AfterReturning, @AfterThrowing, and @Around.

A pointcut is an expression that looks for types and objects on which to apply the aspect’s advices.

Spring 5 Recipes: A Problem-Solution Approach, Marten Deinum, Daniel Rubio, and Josh Long, APress Springer, 2017

For testing the aspects write a simple calculator interface like this:

public interface ArithmeticCalculator {

	public double add(double a, double b);
	public double sub(double a, double b);
	public double mul(double a, double b);
	public double div(double a, double b);

}

Then implementation of this interface:

import org.springframework.stereotype.Component;

@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public double add(double a, double b) {
		double result = a + b;
		System.out.println(a + " + " + b + " = " + result);
		return result;
	}

	@Override
	public double sub(double a, double b) {
		double result = a - b;
		System.out.println(a + " - " +
		 b + " = " + result);
		return result;
	}

	@Override
	public double mul(double a, double b) {
		double result = a * b;
		System.out.println(a + " * " +
		 b + " = " + result);
		return result;
	}

	@Override
	public double div(double a, double b) {
		if (b == 0) {
			throw new IllegalArgumentException("Division by zero");
			}
			double result = a / b;
			System.out.println(a + " / " + b + " = " + result);
			return result;
	}

}

And a Unit Calculator interface for convert the unit:

public interface UnitCalculator {

	public double kilogramToPound(double kilogram);
	public double kilometerToMile(double kilometer);

}

And implementation of this interface:

import org.springframework.stereotype.Component;

@Component("unitCalculator")
public class UnitCalculatorImpl implements UnitCalculator {

	@Override
	public double kilogramToPound(double kilogram) {
		double pound = kilogram * 2.2;
		System.out.println(kilogram + " kilogram = " + pound + " pound");
		return pound;
	}

	@Override
	public double kilometerToMile(double kilometer) {
		double mile = kilometer * 0.62;
		System.out.println(kilometer + " kilometer = " + mile + " mile");
		return mile;
	}

}

For testing the aspects you need to have these dependencies:

<dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjweaver</artifactId>
    	<optional>true</optional>
</dependency>
		
<dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjrt</artifactId>
    	<optional>true</optional>
</dependency>

The first we need to do in order to have an aspect is write that aspect:

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CalculatorLoggingAspect {
	
	private Log log = LogFactory.getLog(this.getClass());
	
	@Before("execution(* ArithmeticCalculator.add(..))")
	public void logBefore() {
		log.info("The method add() begins.");
	}

}

This pointcut expression:

"execution(* ArithmeticCalculator.add(..))"

matches the add() method execution of the ArithmeticCalculator interface. The preceding wildcard in this expression matches any modifier (public, protected, and private) and any return type. The two dots in the argument list match any number of arguments.

Next, create a Spring configuration to scan all POJOs, including the POJO calculator implementation and aspect and including the @EnableAspectJAutoProxy annotation. To enable annotation support in the Spring IoC container, you have to add @EnableAspectJAutoProxy to your configuration classes.

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class CalculatorConfiguration {

}

Now we can test our first Aspect with this test class:

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

class AspectTest {

	@Test
	void test() {
		ApplicationContext context = new AnnotationConfigApplicationContext(CalculatorConfiguration.class);
				
				ArithmeticCalculator arithmeticCalculator =
				context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
				arithmeticCalculator.add(1, 2);
				arithmeticCalculator.sub(4, 3);
				arithmeticCalculator.mul(2, 3);
				arithmeticCalculator.div(4, 2);
				UnitCalculator unitCalculator = context.getBean("unitCalculator", UnitCalculator.class);
				unitCalculator.kilogramToPound(10);
				unitCalculator.kilometerToMile(5);
	}

}

And this is console output of our test:

The method add() begins.
1.0 + 2.0 = 3.0
4.0 - 3.0 = 1.0
2.0 * 3.0 = 6.0
4.0 / 2.0 = 2.0
10.0 kilogram = 22.0 pound
5.0 kilometer = 3.1 mile

If you want to use an advice that runs before every method you can add this advice to the aspect:

@Before("execution(* *.*(..))")
	public void logBeforeAll(JoinPoint joinPoint) {
		log.info("This is logBeforeAll! The method " + joinPoint.getSignature().getName()
				+ "() begins with " + Arrays.toString(joinPoint.getArgs()));
	}

The execution points matched by a pointcut are called join points (in this case the pointcut is “execution(* *.*(..))“). In this term, a pointcut is an expression to match a set of join points, while an advice is the action to take at a particular join point.
For your advice to access the detail of the current join point, you can declare an argument of type JoinPoint in your advice method. Then, you can get access to join point details such as the method name and argument values. Now, you can expand your pointcut to match all methods by changing the class name and method name to wildcards.

The output looks like this:

This is logBeforeAll! The method add() begins with [1.0, 2.0] 
1.0 + 2.0 = 3.0
This is logBeforeAll! The method sub() begins with [4.0, 3.0] 
4.0 - 3.0 = 1.0
This is logBeforeAll! The method mul() begins with [2.0, 3.0]
2.0 * 3.0 = 6.0
This is logBeforeAll! The method div() begins with [4.0, 2.0]
4.0 / 2.0 = 2.0
This is logBeforeAll! The method kilogramToPound() begins with [10.0]
10.0 kilogram = 22.0 pound
This is logBeforeAll! The method kilometerToMile() begins with [5.0]
5.0 kilometer = 3.1 mile

@After Advice

An after advice is executed after a join point finishes and is represented by a method annotated with @After, whenever it returns a result or throws an exception.

@AfterReturning Advice

An after advice is executed regardless of whether a join point returns normally or throws an exception. If you would like to perform logging only when a join point returns, you should replace the after advice with an after returning advice.

@AfterThrowing Advice

An after throwing advice is executed only when an exception is thrown by a join point.

@Around Advice

It gains full control of a join point, so you can combine all the actions of the preceding advices into one single advice. You can even control when, and whether, to proceed with the original join point execution.

A common rule for choosing an advice type is to use the least powerful one that can satisfy your requirements.

About Aliyar Güneş

I’am Aliyar Güneş, a learner and software developer from Istanbul, Turkey. I write C# and Java.
This entry was posted in Spring Framework, Works and tagged , , , , , , , , , . Bookmark the permalink.

Leave a Reply