Geek Culture
Published in

Geek Culture

Learn Aspect-Oriented Programming by Example

This story offers a great way for you to learn Aspect Oriented Programming by studying concrete examples. In particular, I will showcase SpringBoot AOP by implementing 4 Aspects.

Table of Contents:

If you’re the person who wants to skip lengthy descriptions and just look at concrete code, I’ve got you covered:

What is an Aspect?

So there are some great resources out there for an overview of Spring AOP, including this Baeldung article and the official Spring AOP documentation. But since we don’t wanna focus on boring theory and rather keep things practical, here’s a really short summary how AOP works:

Aspect Oriented Programming — Overview

We’ll need the following terms in this tutorial:

@Cacheable: a standard Spring Advice

Let’s start simple and consider an already implemented Advice by Spring, namely the @Cacheable annotation. Say your web service must compute numbers of the Fibonacci series.

If you don’t know what the Fibonacci series is: it’s the series starting with 0 and 1 and each consecutive number is the sum of the previous two numbers.

We implement the Fibonacci computation in a @Service class:

Next, we use this service class in our REST controller:

Our implementation is recursive and thus rather slow. So how do you make your web service faster? One way would be to use a faster algorithm, but let’s solve the problem with Spring’s @Cacheable feature. This annotation creates a cache in the background where all previous results get stored. All we must do, is add the @Cacheable annotation to our method:

Now we’re ready to test our caching mechanism by firing a REST request to http://localhost:8080/api/fibonacci/40. I tried to compute the 40th Fibonacci on my own laptop and here are the results:

Pretty good result eyyy🤙😎

One last thing I’d like to mention: in order to activate Spring’s cacheable feature, you have to add @EnableCaching to a @Configuration class.

Log REST calls with a custom Aspect

That was pretty easy right? So let’s move on to a more advanced use case: now we create a custom Aspect!

Our goal is to create a log message every time some REST method gets called. Since we might wanna add this functionality to future REST methods too, we want to generalize this task in an Aspect:

The first line defines the Pointcut expression, and the subsequent method represents the Advice. Let’s break the two down one by one:

Pointcut:
The Pointcut expression defines the places where our Advice is inserted to. In our case, the Aspect is applied before every method with a @LogMehtodName annotation. Note that @LogMethodName is our custom annotation which we use as Pointcut marker.

Advice:
The advice method is the piece of logic that generalizes a task common to many different objects. In our case, the Advice finds the originating method’s name as well as its calling parameters and logs them to the console.

With our Aspect in place, there are three additional code lines required to get everything working:

That’s it, we’ve implemented our own Advice!🙌 Let’s run a test! We fire a REST request to the web service to compute the 40th Fibonacci number and have a look at the console output:

Method [fibonacci] gets called with parameters [40]

It goes without saying that such log messages will be of great help if you ever must track down bugs in your application.

Performance monitoring with AOP

In the previous example, we used a Pointcut expression of type @Before — here, the Advice runs before the actual method. Let’s switch gears and implement an @Around Pointcut. Such an Advice runs partly before the target method and partly after it.

Our goal now is to monitor the execution time of any REST call. Let’s go ahead and implement the monitoring requirement in a generalized fashion, namely an Aspect:

Pointcut:
Like before, we create a new custom annotation @MonitorTime for marking our Pointcuts.

Advice:
An @Around Aspect should have an argument of type ProceedingJoinPoint. This type has a proceed() method which triggers the execution of the actual target method. So in our Advice, we first query the current time in milliseconds. After the target method is executed, we measure the current time again, and from there we can compute time difference.

Let’s go ahead and mark our target method with the @MonitorTime annotation:

By now, our REST method has quite some Pointcut markers attached to it😉 Anyways, let’s go ahead and test our performance monitoring feature. As before, we compute the 40th Fibonacci number:

Method [fibonacci] gets called with parameters [40]
Execution took [1902ms]

As you can see, this particular REST call took 1902ms. With this @Around Aspect in place, you’re definitely an advanced AOP programmer!💪

Retry mechanism with AOP

Distributed systems can experience concurrency issues. One such example would be when two web service instances are simultaneously trying to access the same record in a database. Oftentimes, such a lock problem can be resolved by retrying the operation. The only requirement here is that the operation is idempotent.

Let’s go ahead and create an Aspect which transparently retries an operation until it succeeds:

Pointcut:
Our Advice runs around any method with the custom annotation @RetryOperation.

Advice:
In the try statement, we run the target method. This method might throw a RuntimeException. If this happens, we increment the numAttempts counter and simply rerun the target method. As soon as the target method succeeds, we exit the Advice.

For demonstration purposes, let’s create a REST method for storing a String. This method will fail 50% of the time:

Thanks to our @RetryOperation annotation, the above method will be retried until it succeeds. Moreover, we use our @LogMethodName annotation so we can see every method invocation. Let’s go ahead and test our new REST endpoint; for this purpose we fire a REST request to http://localhost:8080/api/storeData?data=hello-world.

Method [storeData] gets called with parameters [hello-world]
Method [storeData] gets called with parameters [hello-world]
Method [storeData] gets called with parameters [hello-world]
Pretend everything went fine

In the above case, the operation failed 2 times and only succeeded on the third try.

Conclusion

Congrats, you’re a professional AOP programmer now🥳🚀 You can find a fully working web service with all Aspects on my Github repo:

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

First published on DEV.to.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Philipp Gysel

I love finance and computer science. I’m interested in long-term investing and Java/SpringBoot applications.