top of page
Writer's pictureThe Tech Platform

Aspect Oriented Programming with SpringBoot



What is AOP?

AOP is a programming paradigm whose main aim is to increase modularity by allowing the separation of cross-cutting concerns. AOP does this by adding additional behavior to the existing code without modifying the code itself. It provides functionalities to add new code and behavior separately.


The AOP provides us the flexibility to define all the common concerns in one class and we can define where all these concerns are used and how they are used in a class without modifying it is existing code and applying the new features. This class with all the cross-cutting concerns is modularized into a specific class call Aspect. In order to use AOP, we need to implement the responsibility of aspect in our class called Advice.


There are some key terminologies in Aspect-Oriented Programming that are shown in the tabular manner below as follows:

TERMINOLOGIES

ACTION PERFORMED

Aspect

It is a module that provides cross-cutting concerns by encapsulating the advice and pointcuts. An application can have any number of aspects in it. In order to use it, we need to annotate a class with @Aspect annotation.

Advice

It is an action that is taken before or after method execution. An action is a block of code that gets invoked during the execution of a program. The Spring AOP framework support five type of advice before, after, after-returning, after-throwing, and around advice. The advice is taken for join points. Advice is applied over the target object.

Pointcuts

Pointcuts are the set of join points where the advice is executed. These pointcuts are defined using the expression or pattern.

Join Points

It is a place in an application where the AOP Aspect is applied. A join point can be a method execution, exception handling, etc.

Target Object

It is an object where the advice is applied. These target objects are proxied

Proxied

Target objects are proxied which means during the runtime the target methods are overridden and depending on method configuration, the advice is included in the target object.

Weaving

The process of linking the application with the aspect is called weaving. It can be done at load time, compile time and run time.

Why we need AOP?

Most bigger companies have programming guidelines, and so does mine. One of our guidelines states that every REST endpoint execution must be logged (name of the Java method + the parameters).


Here’s how you could solve this:

@RestController
public class MyRestController 
{
    @GetMapping(path = "/api/hello/")
    public String hello() 
    {
        System.out.println("Method [hello] gets called with 0 parameters");
        return "Hello world!";
    }
}

The code snippet above does the following:

  • @RestController: Make sure SpringBoot knows this class contains REST endpoints

  • @GetMapping: A method which replies to HTTP GET requests

  • System.out.println(...): adhere to the aforementioned coding guidelines

  • return value: the method simply returns a greeting message of type String

In a realistic app, you will have many such REST calls, in many different classes. Doing the exact same logging in all those methods is cumbersome. Moreover, if the coding guidelines slightly change, you will have to change the logging message in each method.


Here’s where AOP comes to the rescue: with the help of AOP, we can nicely add common functionality to many different places in our code, without interfering with existing code. In literature jargon, AOP is all about the separation of cross-cutting concerns. In more human understandable language, AOP enables the modularization of common tasks across different objects.


Maven Dependency

To start using AOP in SpringBoot with AspectJ annotations, we need to import the following dependencies in our pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

Advice

Let’s create a function which performs our required logging in a generic way:

public void logMethodExecution(JoinPoint joinPoint) 
{
    String method = joinPoint.getSignature().getName();
    String params = Arrays.toString(joinPoint.getArgs());
    System.out.println("Method [" + method + "] gets called with parameters " + params);
}

This generic function is called an Advice. Note that it can log the name and parameters of any method; let’s break the Advice down step by step:

  • JoinPoint: this object contains all information about the Join point, that is, the "location" where our Aspect will be inserted. In our case, this would be the REST method for which we want to create a log message for.

  • joinPoint.getSignature() and joinPoint.getArgs() extracts the method signature as well as the calling arguments

  • System.out.println(...): do the necessary logging


Pointcut

So where do we want to insert the above Advise method? Well, we want each REST endpoint to be tracked. While there are many ways to mark our REST endpoints, we choose to use a custom annotation to define the Pointcut:

@Before("@annotation(LogMethod)")
public void logMethodExecution(JoinPoint joinPoint) {...}

As you can see, the Pointcut definition is just a one-liner:

  • @Before: we run the Advice before the REST call gets answered

  • @annotation: we mark Pointcuts via an annotation

  • LogMethod: this is the name of our custom annotation

Now we’re ready to mark our REST method with our custom annotation:

@LogMethod
@GetMapping(path = "/api/hello/")
public String hello() 
{
    return "Hello world!";
}

Note that we prepended the REST method with the annotation @LogMethod. Moreover, we removed the logging inside the method, this is now done by our Aspect.


Aspect

An Aspect is a Pointcut plus an Advice. So, let’s put the two together, and we get:

@Aspect
@Component
public class LoggingAspect 
{
    @Before("@annotation(LogMethod)")
    public void logMethodName(JoinPoint joinPoint) 
    {
        String method = joinPoint.getSignature().getName();
        String params = Arrays.toString(joinPoint.getArgs());
        System.out.println("Method [" + method + "] gets called 
        with parameters " + params);
    }
}

Here’s what we have:

  • @Aspect: SpringBoot expects all Aspects to be in classes annotated with @Aspect

  • @Before(...): the Pointcut

  • logMethodName(...){...}: the Advice

So all we did here was just bring together the previously shown Pointcut expression plus the Advice, and wrap everything in a class. Bring out the champagne, we have our Aspect all finished and working 🥂


Enable AOP

@Configuration
@EnableAspectJAutoProxy
public class AspectConfig 
{
}

Remember that we want to be able to work with Beans when using Spring. At the moment, our @RestController class only contains the REST-call logic, but not our Advice. Spring can create Proxies for such Beans which contain this additional logic (the Advice), and this is enabled by @EnableAspectJAutoProxy.



The Tech Platform

0 comments

Comments


bottom of page