Spring AOP
Table of contents
Introducing Spring AOP
Spring AOP (Aspect-Oriented Programming) is a mechanism for modularizing cross-cutting concerns in your application. It allows you to define aspects, which are classes that encapsulate behavior that cuts across multiple classes. An aspect can be thought of as a module that implements a particular feature, such as logging, security, or transaction management.
Spring AOP works by using proxies to dynamically weave the aspects into the target objects at runtime. A proxy is an object that acts as an intermediary between the client and the target object, and it can be used to intercept method invocations and add additional behavior. When you use Spring AOP, the framework generates proxies for your target objects and advises them with the aspects you’ve defined. This means that the aspects are woven into the bytecode of the target objects, and their behavior is executed when the target objects are used.
In short, Spring AOP provides a flexible way to add behavior to your application without affecting the code of the target objects. It helps to keep your code organized and clean by encapsulating cross-cutting concerns into separate aspects.
Aspect oriented Programming is programming paradigm which is analogous to object oriented programming. Key unit of object oriented programming is class, similarly key unit for AOP is Aspect. Aspect enable modularisation of concerns such as transaction management, it cut across multiple classes and types. It also refers as a crosscutting concerns.
Why AOP?
It provides pluggable way to apply concern before, after or around business logic. Lets understand with the help of logging. You have put logging in different classes but for some reasons, if you want to remove logging now, you have to make changes in all classes but you can easily solve this by using aspect. If you want to remove logging, you just need to unplug that aspect.
AOP concepts
Spring AOP has the following key components:
-
Aspects: Aspects are classes that encapsulate the behavior for a particular feature or concern, such as logging, security, or transaction management.
-
Join Points: A join point is a point in the execution of the program where an aspect can be applied. Examples of join points include method invocations, field access, and exception handling.
-
Pointcuts: Pointcuts are expressions that determine which join points an aspect should be applied to. They can be defined using regular expressions, method signatures, or a combination of both.
-
Advice: Advice is the actual code that gets executed when a join point matched by a pointcut is reached. There are five types of advice in Spring AOP: before, after, after-returning, after-throwing, and around.
-
Proxies: Proxies are objects that act as intermediaries between the client and the target object. They are generated by the AOP framework and advised with the aspects you’ve defined. When you use a proxy, the behavior of the aspects is executed when the target object is used.
-
Weaving: Weaving is the process of applying aspects to target objects to create advised objects. Spring AOP supports both compile-time weaving, where the aspects are woven into the bytecode of the target objects during compilation, and runtime weaving, where the aspects are woven into the target objects at runtime using proxies.
When you use Spring AOP, you define aspects that encapsulate the behavior you want to add to your application. You also define pointcuts that determine when the aspects should be applied, and you define advice that specifies the behavior to be executed when the join points matched by the pointcuts are reached. The AOP framework generates proxies for your target objects and advises them with the aspects you’ve defined, which means that the aspects are woven into the target objects and their behavior is executed when the target objects are used.
- Aspect: An Aspect is a class that implements concerns that cut across different classes such as logging. It is just a name.
- Joint Point : It is a point in execution of program such as execution of method. In Spring AOP, a join point always represents a method execution.
- Advice : Action taken by aspect at particular join point. For example: Before execution of getEmployeeName() method, put logging. So here, we are using before advice.
- Pointcut : Pointcut is an expression that decides execution of advice at matched joint point. Spring uses the AspectJ pointcut expression language by default.
- Target object : These are the objects on which advices are applied. For example: There are the object on which you want to apply logging on joint point.
- AOP proxy : Spring will create JDK dynamic proxy to create proxy class around target object with advice invocations.
- Weaving : The process of creating proxy objects from target object may be termed as weaving.
Types of Advices
Advice is action taken by aspect at particular joint point.
- Before Advice: it executes before a join point.
- After Returning Advice: it executes after a joint point completes without any exception.
- After Throwing Advice: it executes if method exits by throwing an exception.
- After Advice: it executes after a join point regardless of outcome.
- Around Advice: It executes before and after a join point.
Spring AOP Examples
Logging
Here is an example of an aspect that implements logging using Spring AOP:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.demo.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
logger.info("Entering method: {}", joinPoint.getSignature().toShortString());
}
@After("execution(* com.example.demo.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
logger.info("Exiting method: {}", joinPoint.getSignature().toShortString());
}
}
This aspect uses the @Before and @After annotations to define advice that should be executed before and after methods in the com.example.demo.service package. The advice uses the SLF4J logger to log messages indicating when methods are entered and exited.
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.demo.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
logger.info("Entering method: {} with arguments: {}",
joinPoint.getSignature().toShortString(),
Arrays.toString(joinPoint.getArgs()));
}
@After("execution(* com.example.demo.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
logger.info("Exiting method: {} with result: {}",
joinPoint.getSignature().toShortString(),
joinPoint.getSignature().toShortString());
}
}
This aspect uses the @Before and @After annotations to define advice that should be executed before and after methods in the com.example.demo.service package. The advice logs messages indicating when methods are entered and exited, along with their arguments and results.
Transactions
Here is an example of an aspect that implements transaction management using Spring AOP:
@Aspect
@Component
@Transactional
public class TransactionAspect {
@Around("execution(* com.example.demo.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Exception ex) {
// rollback the transaction here
throw ex;
}
}
}
This aspect uses the @Around annotation to define advice that should be executed around methods in the com.example.demo.service package. The advice uses the ProceedingJoinPoint to proceed with the original method call and manage the transaction by rolling it back in case of an exception. The @Transactional annotation is used to enable transaction management for the aspect.
Note that in order to use transactions in your application, you will also need to configure a transaction manager, such as JPA, Hibernate, or JDBC, and enable transaction management in your Spring Boot configuration.
Exception handling
@Aspect
@Component
public class ExceptionHandlingAspect {
@AfterThrowing(pointcut = "execution(* com.example.demo.service.*.*(..))", throwing = "ex")
public void handleException(JoinPoint joinPoint, Exception ex) {
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
logger.error("Exception in method: {} with message: {}",
joinPoint.getSignature().toShortString(),
ex.getMessage());
}
}
This aspect uses the @AfterThrowing annotation to define advice that should be executed after a method in the com.example.demo.service package throws an exception. The advice logs an error message indicating the method that threw the exception and the exception message.
Performance monitoring example:
@Aspect
@Component
public class PerformanceMonitoringAspect {
@Around("execution(* com.example.demo.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - startTime;
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
logger.info("Method: {} took {} ms to execute",
joinPoint.getSignature().toShortString(),
elapsedTime);
return result;
}
}
This aspect uses the @Around annotation to define advice that should be executed around methods in the com.example.demo.service package.