Aspect Oriented Programming

Gaurav J SomaniGaurav J Somani
5 min read

When building applications in Spring boot, we often run into scenarios where we need to apply the same logic across multiple parts of the code - like logging, some pre-processing, security checks, performance monitoring, transaction management, etc. Writing this logic repeatedly is not only just boring but it also makes our code a lot harder to maintain. And that’s where, AOP comes to rescue!

So lets get a deeper understanding of AOP, and I’ll be talking wrt Spring boot, though you can use it with other languages/frameworks as it is just a programming paradigm (like in Python where you can use decorators to apply AOP)

Intro

AOP addresses the problem of cross-cutting concerns, which would be any kind of code that is repeated in different methods. To put it simply, it’s just an interceptor to intercept some processes, for example, when a method is to be executed, Spring AOP can hijack the executing method, and add extra functionality before or after the method execution.

Lets start with an example

So lets say we want to log various things, now logging is something which is not directly related to our main business logic and we dont wanna mix that with our core business logic, but we still need it anywhere. And it’d be great if we can handle such things separately without cluttering our actual code. AOP can help us there! But before diving into the usual AOP terms like JoinPoint, Pointcut, and Advice, let’s start with a simple example to understand its real-world use case.

Problem: Repeated logging

Now here I’ll use logging to show the use case of AOP, but remember we can use AOP for pre processing, security checks, etc.

Imagine we have a service class with various methods. And in each method we wanna log something, like some metadata, or the amount of time the methods takes to execute. We can use Logger everywhere but as I explained earlier, it can be separated.

The AOP solution

Instead of logging directly in the XyzService class, we can define an aspect to handle logging automatically.

@Aspect
@Component
public class LoggingAspect {

    @Around("execution(* pathtoyourservice.XyzService.*(..))")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();

        LOG.info("Executing method: {} in {} with arguments {}", methodName, joinPoint.getSignature().getDeclaringType(), joinPoint.getArgs());
        Object result = null;
        try {
             result = joinPoint.proceed(); //execute the actual method
        }catch (Exception e){
            LOG.error("Exception in method {} - {}", methodName, e.getMessage(), e);
        }

        long executionTime = System.currentTimeMillis() - startTime;
        LOG.info("Method {} executed in {} ms", methodName, executionTime);
        return result;
    }
}

That’s it 😅!

What just happened?

  • Around - this is called an advice. Its supposedly the most powerful advice. Around advice can perform custom behaviour before and after the method invocation. The first parameter of the advice method must be of type ProceedingJoinPoint. Within the body of the advice, calling proceed() on the ProceedingJoinPoint causes the underlying method to execute. AOP has other advices as well like @Before, @After to name a few. It’s pretty clear by their names when to use them.

  • The LoggingAspect intercepts the methods (we have defined that in @Around(….)), and logs before and after execution.

Understanding AOP terms

  1. Aspect - a class that contains reusable logic (like logging) that we apply to multiple places.

  2. JoinPoint - A specific point in the execution of the program.

  3. Advice - the logic that runs before, after or around the method.

    a. Before: These advices runs before the execution of join point methods. We can use @Before annotation to mark an advice type as Before advice.

    b. After: an advice that gets executed after the join point method finishes executing, whether normally or by throwing an exception. We can create after advice using @After annotation.

    c. Around: already explained

    d. After Returning Advice: Sometimes we want advice methods to execute only if the join point method executes normally, i.e without an exception . We can use @AfterReturning annotation to mark a method as after returning advice.

    e. After Throwing Advice: This advice gets executed only when join point method throws exception, we can use it to rollback the transaction declaratively. We use @AfterThrowing annotation for this type of advice.

  4. Pointcut - Pointcuts allow you to specify where you want your advice to be applied. Often you specify these pointcuts using explicit class and method names or through regular expressions that define matching class and method name patterns.

Custom annotation

Consider this as a bonus 😁

Suppose you have to apply aspect to methods situated in different folders and its quite difficult to target them all using regular expression as we just did. Lets take an example for pre-processing.

In this case we can create a custom annotation and mark the methods with that annotation and then use that in our Pointcut.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreProcessInput {
    Enums.Action action();
}

@PreProcessInput(action = Enums.Action.UPDATE)
 public boolean update(Input input) {
     ...
}

We define an annotation to mark the methods where logging should be applied and annotate the methods where we need to do some kind of pre-processing.

Define an aspect

@Aspect
@Component
public class PreProcessorAspect {

    @Before(value = "@annotation(preProcessInput)")
    public void preProcess(JoinPoint joinPoint, PreProcessInput preProcessInput) {
        //logic for pre-processing
    }

}

As you can clearly see we have used @annotation(preProcessInput) instead of some regex. So here we have also learnt how can custom annotation be used with AOP!

Common use cases of AOP

(I have referred stack overflow for this)

  • A website in which every page is generated by a Python function. I'd like to take a class and make all of the webpages generated by that class password-protected. AOP comes to the rescue; before each function is called, I do the appropriate session checking and redirect if necessary.

  • I'd like to do some logging and profiling on a bunch of functions in my program during its actual usage. AOP lets me calculate timing and print data to log files without actually modifying any of these functions.

Conclusion

So next time you find yourself writing the same code in multiple places, think about whether AOP can make your life easier.

So that’s it for this blog! I know I’m writing a blog after a long time but lets meet sometime soon.

Thank you for staying till the end!

1
Subscribe to my newsletter

Read articles from Gaurav J Somani directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gaurav J Somani
Gaurav J Somani

Hello, I'm Gaurav - a computer engineering graduate who loves development. When I'm not coding, you'll find me reading a book or playing some sport. Join me as I'll share my insights of whatever I learn.