Spring Boot AOP Tutorial
Watch on YouTubeSpring Boot AOP (Aspect-Oriented Programming)
Keep cross-cutting concerns such as logging, security, transactions, and timing separate from business logic.
What is AOP?
Some behavior affects many parts of an application. Logging, authorization, transaction management, caching, and performance measurement are called cross-cutting concerns. AOP moves that repeated behavior into an aspect and applies it to selected method executions.
Why use AOP?
Logging, authorization, transactions, auditing, caching, and performance measurement affect many services. These are cross-cutting concerns. AOP centralizes the repeated technical behavior so business methods stay focused on business decisions.
| Concern | AOP behavior | Real-world example |
|---|---|---|
| Logging | Record method and duration | Trace an order request |
| Security | Check an annotation | Protect refund approval |
| Transactions | Commit or roll back work | Save order and inventory together |
| Auditing | Record important changes | Track address updates |
| Performance | Measure execution time | Find slow service calls |
AOP Terminology
| Term | Meaning |
|---|---|
| Aspect | A class containing cross-cutting behavior. |
| Join point | A point during execution where behavior could be applied; Spring AOP mainly uses method execution. |
| Pointcut | A rule that selects which join points should be intercepted. |
| Advice | The action that runs before, after, or around the selected method. |
| Target | The original bean containing the business method. |
| Proxy | The Spring-created object that intercepts calls to the target. |
| Weaving | The process of connecting an aspect to target behavior; Spring commonly does this through runtime proxies. |
Advice Types
@Beforeruns before the matched method.@AfterReturningruns after a successful return.-
@AfterThrowingruns when the method throws an exception. -
@Afterruns after completion, whether successful or failed. -
@Aroundsurrounds the method and can decide whether and when to call it.
Advice examples
@Aspect
@Component
class AuditAspect {
@Before("execution(* com.javacodeex.orders.service..*(..))")
void beforeServiceCall(JoinPoint joinPoint) {
log.info("Starting {}", joinPoint.getSignature().toShortString());
}
@AfterReturning(
pointcut = "execution(* com.javacodeex.orders.service..*(..))",
returning = "result")
void afterSuccess(JoinPoint joinPoint, Object result) {
log.info("Completed {}", joinPoint.getSignature().toShortString());
}
@AfterThrowing(
pointcut = "execution(* com.javacodeex.orders.service..*(..))",
throwing = "error")
void afterFailure(JoinPoint joinPoint, Throwable error) {
log.warn("{} failed: {}", joinPoint.getSignature(), error.getMessage());
}
}
Around advice in detail
Around advice receives a ProceedingJoinPoint. Calling
proceed() runs the target method. A finally
block runs even when the target throws an exception.
@Around("execution(* com.javacodeex.orders.service..*(..))")
Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
try {
return joinPoint.proceed();
} catch (Throwable error) {
log.error("Failure in {}", joinPoint.getSignature(), error);
throw error;
} finally {
long elapsedMs = (System.nanoTime() - start) / 1_000_000;
log.info("{} took {} ms", joinPoint.getSignature(), elapsedMs);
}
}
proceed(), the target method does not run.
Pointcuts
A pointcut expression defines what should be intercepted. Keep pointcuts narrow and readable so an aspect does not unexpectedly affect unrelated code.
// Any method in the service package
@Pointcut("execution(* com.javacodeex.orders.service..*(..))")
private void serviceMethods() { }
// Only methods marked with an annotation
@Pointcut("@annotation(com.javacodeex.orders.TrackTime)")
private void trackedMethods() { }
The execution expression above matches methods inside the
service package. An annotation pointcut matches only methods explicitly
marked with a custom annotation.
Join points
A join point is a possible point where an aspect could run. Full AspectJ supports more join point types, but Spring AOP is proxy-based and mainly intercepts method execution on Spring-managed Beans.
Common pointcut designators
| Designator | Meaning | Example |
|---|---|---|
execution |
Matches method signatures | execution(* service..*(..)) |
within |
Matches types or packages | within(com.javacodeex.orders.service..*) |
@annotation |
Matches annotated methods | @annotation(TrackTime) |
&& and || |
Combines pointcut rules | serviceMethods() && trackedMethods() |
Logging Aspect Example
@Aspect
@Component
class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
@Around("execution(* com.javacodeex.orders.service..*(..))")
Object logCall(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long time = System.currentTimeMillis() - start;
log.info("{} completed in {} ms", joinPoint.getSignature(), time);
}
}
}
ProceedingJoinPoint represents the intercepted call.
proceed() invokes the real method; without it, an around
advice can prevent the target method from running.
Real-time logging example
A production logging aspect should record method, status, correlation ID, and duration without logging passwords, tokens, or full request bodies.
@Around("@annotation(com.javacodeex.orders.Logged)")
Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable {
String operation = joinPoint.getSignature().toShortString();
Instant started = Instant.now();
try {
Object result = joinPoint.proceed();
log.info("operation={} status=success durationMs={}",
operation, Duration.between(started, Instant.now()).toMillis());
return result;
} catch (Throwable error) {
log.warn("operation={} status=failure type={}",
operation, error.getClass().getSimpleName());
throw error;
}
}
Authorization and auditing
Spring Security uses method interception for annotations such as
@PreAuthorize. A custom aspect can add an audit event, but
it should complement the security filter chain rather than replace it.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audited { }
@Audited
@PreAuthorize("hasRole('MANAGER')")
public void approveRefund(Long orderId) {
// Protected business operation
}
Caching and performance
Caching and timing are common AOP use cases. Define cache keys and invalidation rules carefully so an aspect does not return stale data.
@Service
class ProductService {
@Cacheable(cacheNames = "products", key = "#id")
public Product findById(Long id) {
return repository.findById(id).orElseThrow();
}
}
Custom Annotation AOP
Annotations make an aspect opt-in and easier to understand at the call site.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime { }
@TrackTime
public Report createReport() {
return reportService.create();
}
Use an annotation pointcut to apply timing only to methods marked with
@TrackTime.
AOP and Transactions
@Transactional is a familiar Spring AOP use case. Spring
creates a proxy that opens a transaction before the method and commits
or rolls it back afterward.
@Service
class OrderService {
@Transactional
public void placeOrder(OrderRequest request) {
orderRepository.save(toOrder(request));
inventoryService.reserve(request.items());
}
}
Proxy limitations and common mistakes
- Self-invocation: one method calling another method on the same object can bypass the proxy.
-
Objects created with
new: Spring cannot apply advice to objects it does not manage. - Private and final methods: proxy-based Spring AOP may not intercept them as expected.
- Broad pointcuts: an overly broad rule can log health checks or sensitive data unexpectedly.
- Hidden behavior: keep important security and transaction boundaries visible in design and tests.
Enable AOP in Spring Boot
Add the AOP starter to enable the infrastructure required by component-based aspects.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Good Use Cases and Pitfalls
| Good use | Be careful about |
|---|---|
| Logging and timing | Do not log passwords, tokens, or sensitive payloads. |
| Authorization checks | Make security rules visible and test them. |
| Transactions | Understand proxy boundaries and rollback rules. |
| Auditing | Keep audit data reliable and avoid hidden side effects. |
| Caching | Define keys and invalidation behavior clearly. |
Continue learning
