Advanced Spring: AOP and Global Exception Handling
Clean Controllers Through AOP
A common smell in Java applications is the “Try-Catch” block repeated in every controller method. By leveraging AOP and @ControllerAdvice, we can extract this boilerplate into centralized, cross-cutting components.
Core Concepts
1. Aspect-Oriented Programming (AOP)
Allows you to “hook” into method execution without modifying the source code.
- Join Point: A point during the execution of a program (e.g., method call).
- Advice: Action taken by an aspect at a join point (Before, After, Around).
- Pointcut: Predicate that matches join points (e.g., “all methods in the service package”).
2. @ControllerAdvice
A specialized @Component that allows for handling exceptions across the entire application in one place.
Practice Exercise: AOP Performance Logger and Global Error Handler
Step 1: The AOP Aspect
We will log the execution time of every method marked with a custom @LogTime annotation.
@Aspect
@Component
public class PerformanceAspect {
@Around("@annotation(com.example.LogTime)")
public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
Step 2: Global Exception Handler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException ex) {
ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse("INTERNAL_ERROR", "Something went wrong.");
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Why This Works
- Separation of Concerns: Your controllers focus purely on mapping requests to business logic. They don’t need to know how to log or how to format error responses.
- Proxy Pattern: Spring AOP works by creating a Dynamic Proxy around your
@Service. When a method is called, the proxy executes the Aspect code first, then the core method, then the Aspect code again (in the case of@Around).
Summary
AOP and @ControllerAdvice are the keys to a “Thin Controller” architecture. By moving infrastructure concerns into aspects and advice, you make your code more readable, more testable, and significantly easier to maintain as it grows.