Aspect Oriented Programming with Java and SpringBoot
Tags: java, aop, springboot, beginners
Published: 2021-01-06 13:26:26

This blog covers the basics of **Aspect Oriented Programming** (AOP). I’ll show a simple working example of AOP: a common task like logging of REST calls can be generically defined in an **Advice**, which can then be applied to various target places in our code (so called **Pointcuts**) via an **Aspect**.

For the impatient: you can find the end result on my Github repo🏎️

{% github pmgysel/aop-demo no-readme %}

# Programming Paradigms

Back in the 1996, when [Java](https://www.java.com/en/) 1.0 was released, Java developers were all hyped up about [object oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), also called OOP. While OOP was one of the foundational drivers for Java, the programming language itself has gone a long way since then and now supports many [programming paradigms](https://en.wikipedia.org/wiki/Programming_paradigm).

Here’s a list of the primary programming paradigms supported in Java (in historical order):
* **Procedural programming**
* **Object oriented programming** (OOP)
* **Functional programming**
* **Aspect oriented programming** (AOP)

For this post, we focus on [AOP](https://en.wikipedia.org/wiki/Aspect-oriented_programming) and show how to create Aspects. You’ll learn both the basics of AOP as well as how to use it with [SpringBoot](https://spring.io/projects/spring-boot).

# Why AOP?

Most bigger companies have programming guidelines, and so does mine. One of our guidelines states that every [REST](https://spring.io/guides/gs/rest-service/) endpoint execution must be logged (name of the Java method + the parameters).

Here’s how you could solve this:

```java
@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](https://en.wikipedia.org/wiki/Aspect-oriented_programming) 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](https://spring.io/projects/spring-boot) with [AspectJ annotations](https://www.eclipse.org/aspectj), we need to import the following dependencies in our `pom.xml`:

```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:

```java
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](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-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](https://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lang/JoinPoint.html), 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](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-pointcuts):

```java
@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:

```java
@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](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-at-aspectj) is a Pointcut plus an Advice. So, let’s put the two together, and we get:

```java
@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

To wrap up, we have to [enable AspectJ for our Spring configuration](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-aspectj-support):

```java
@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](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-understanding-aop-proxies) for such Beans which contain this additional logic (the Advice), and this is enabled by `@EnableAspectJAutoProxy`.

# Conclusion

That’s all! You now have a fully working example of AOP😀💪🍾

We implemented an Advice to be run anytime a method with annotation `@LogMethod` is executed. Thanks to our AOP approach, we can add this annotation to future REST methods which will then be advised with the same Aspect!

Checkout the fully working example on Github:

{% github pmgysel/aop-demo %}

This Github repo also contains a second advice of type `@Around`: Each time a REST method is called, we log the execution time. This can come in handy to measure, monitor and compare the performance of different REST endpoints.

Thanks for reading, please leave a comment if you have any feedback or questions!😀

_______________

# Further readings

For the curious, here’s some more reading material:

[**AOP primer and Advice types**](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-introduction-defn): 
In our example, we used an Advice of type [@Before](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-advice-before). Alternatively, you can use [@After](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-advice-after-finally), [@Around](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-ataspectj-around-advice) or [@AfterThrowing](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-advice-after-throwing).

[**Pointcuts**](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-pointcuts) are predicates that match a set of Join points. We used annotation-driven Pointcuts, but Spring supports many more designator types. For example, Spring supports `execution` Pointcuts where a method name has to match a given pattern.

[**AOP Proxies**](https://docs.spring.io/spring-framework/docs/5.0.0.RELEASE/spring-framework-reference/core.html#aop-introduction-proxies): an explanation in the official Spring documentation.