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:
- Enable Auto Proxy
- Create Aspects
- Create Pointcut expressions
- 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
Post a Comment