Advice Types

Five Kinds of Advice

AnnotationRunsTypical use
@Beforebefore the methodvalidation, audit "attempt" logs
@AfterReturningafter a normal return (can read the result)audit success, metrics
@AfterThrowingafter an exception (can read it)alerting, error metrics
@Afteralways, like finallycleanup, MDC clear
@Aroundwraps the call; you decide whether to call proceed()timing, retries, caching

All Advice Types Together

@Aspect
@Component
@Slf4j
public class OrderAuditAspect {

    @Pointcut("execution(* com.shopease.service.OrderService.placeOrder(..))")
    void placeOrder() {}

    @Before("placeOrder() && args(request)")
    public void before(PlaceOrderRequest request) {
        log.info("Order attempt by user {}", request.userId());
    }

    @AfterReturning(pointcut = "placeOrder()", returning = "order")
    public void success(OrderResponse order) {
        log.info("Order {} placed, total {}", order.id(), order.total());
    }

    @AfterThrowing(pointcut = "placeOrder()", throwing = "ex")
    public void failure(Exception ex) {
        log.error("Order failed: {}", ex.getMessage());
    }

    @After("placeOrder()")
    public void always() {
        MDC.clear();
    }
}

Example

curl -X POST localhost:8080/api/orders -H "Content-Type: application/json" -d '{"userId":7,"items":[{"productId":3,"quantity":1}]}'

Example output

INFO  OrderAuditAspect : Order attempt by user 7
INFO  OrderAuditAspect : Order 1001 placed, total 54999.00

Ordering Several Aspects

When several aspects match the same method, put @Order on the aspect classes: the lower number runs first on the way in and last on the way out.

request  -> SecurityAspect(@Order 1) -> TimingAspect(@Order 2) -> OrderService.placeOrder()
response <- SecurityAspect            <- TimingAspect             <- return value