Spring with Kotlin - Spring AOP

The concept of Aspect-Oriented Programming (AOP) is actually pretty simple. Let's say your manager asks you to implement timestamp logging whenever any method in your application gets executed. Your immediate solution will be to go to each method and add a logger line in the beginning. If you have 10-20 or even 50 methods, that doesn't sound like too much of a deal. But what if you have thousands of methods in your big app or your tiny app scales and all the other developers forget to add logging functionality? You surely need to find a better solution! Inheritance and Delegation both would still need updating each and every class and method inside.

How do we solve this business problem as most of our basic approaches don't work? That's exactly where AOP comes into the picture. AOP is based on Aspects. An Aspect encapsulates cross-cutting logic, can be reused at multiple locations, and can be applied based on configuration. AOP typically uses a proxy design pattern. What's that? Let's say you have the main app calling the target object's method. The main app will never know there's an Aspect monitoring its method call. The Aspect can then be used to perform various operations behind the scenes.

Here's how Aspect work:

What are some of the advantages of AOP?

  • Code at One place instead of being scattered all over the application 
  • Code Reuse
  • Business code becomes cleaner
  • No need to change existing business code

Any disadvantages?

  • Too many aspects - App flow is hard to follow
  • Minor performance cost - Depends on the type of weaving

How about the use cases?

Typical use cases include Logging, Security, Transactions, Auditing, Exception handling, API management, etc.

Key AOP terminologies: 

  • Aspect: Module of Code for cross-cutting concerns. This is a simple Kotlin/Java class that can hold multiple Advices.  
  • Advice: What action should be taken, when it should be applied! This is defined as a method inside an Aspect. The main logic for any app monitoring functionality goes in here.
  • Pointcut: A predicate expression for where Advice should be applied. Pointcut expression helps you find the target method with Pointcut Expression Language (PEL).
  • JoinPoint: When to apply code during program execution. JoinPoint has access to the target method handler, signature, params, return value.
  • Weaving: Connecting Aspects to target objects to create an advised object. Some of the types of weaving are Compile-time, Load-time, and Run-time.
When it comes to real-world implementation of AOP, there are two frameworks - Spring AOP and AspectJ. Here's how they are compared: 

AspectJ Spring AOP
1. The original framework 1. Created on top of AspectJ
2. Complex to configure 2. Light weight, easy to configure
3. Supports compile-time, load-time and run-time weaving. 3. Supports only run-time weaving, comes with a performance cost
4. Works with any POJO 4. Works with only Spring beans from the container

Now, which framework to choose? Go ahead with Spring AOP and switch over to AspectJ for performance enhancements later. We will stick to Spring AOP for simplicity. 

"Make it work, make it right, make it fast" - Kent Beck

As far as the project setup goes, we need the AspectJ library as a dependency since Spring AOP uses it in the background. We can include Spring AOP and other Spring libraries as per our needs. 

Steps for writing AOP code:

  1. Enable Auto Proxy
  2. Create Aspects
  3. Create Pointcut expressions
  4. Develop Advices
Let's have a look at an example step by step:

1. Enable Auto Proxy:

Annotate Spring Config class with @EnableAspectJAutoProxy to enable Spring AOP proxy support

2. Create Aspects:

Annotate aspect class with @Aspect and @Component so that it would be scanned by Spring. The order of execution is undefined by default. We can use @Order with int value to explicitly assign preferences. Remember, lower numbers have higher preference, and execution of the same order numbers is undefined. 

3. Create Pointcut expressions: 

Pointcut expressions are predicate expressions for where Advice should be applied. The PEL (Pointcut Expression Language) matches return type, modifier, name, params of the target method. It does so by using wildcards. We can also reuse and combine multiple Pointcut expressions. There's too much to learn in one blog. This is a great tutorial to know more about Pointcuts.

4. Develop Advices:

There are 5 types of Advice supported by Spring AOP.

@Before: This Advice gets called right before the target method's execution.
@AfterReturning: This Advice gets executed after the target method is run. We will have access to the returning value. Make it nullable in the code (for Kotlin of course) if the method doesn't return anything.
@AfterThrowing: This Advice gets executed after an exception is thrown from the target method. We will have access to the exception object and we can catch it inside or return something else entirely from the Advice.
@After: This Advice gets executed regardless of returning success results or exceptions from the target method. It is very similar to finally block in try-catch-finally. Note that this Advice gets called before @AfterReturning and @AfterThrowing and does not have access to the exception/returning value.
@Around: This Advice gets executed before and after the target method execution. So its a combination of @Before and @After advice. It can also handle the execution of the target method using ProceedingJoinPoint. Typical use cases include logging how much time the target method takes to run. 

Before ending this blog here are a few important takeaways:

  • Keep the code small, fast and don't perform expensive operations - remember you are in a spy network where no business logic should be written.
  • Make target class/method 'open'. This is a huge pain as Kotlin's classes/methods are always final by-default.
  • Make Spring Config class 'open' as well
  • Make returning param nullable. Kotlin will throw an exception if the method doesn't return anything.
There's a lot to learn about AOP but I hope you will start thinking more about its professional use cases after reading this blog. If you have reached till the end, thanks and stay tuned for more!

PS - All the working code is available on my GitHub. Happy coding :)

Comments

Popular Posts