Mastering Spring AOP: All Four Types of Advice Explained
Spring AOP provides five distinct advice types—@Before, @AfterReturning, @AfterThrowing, @After, and @Around—each with specific execution timing; this guide explains their purposes, execution order, common pitfalls, and offers a complete SpringBoot example with code, Maven setup, and logging demonstrations.
In SpringBoot/Java backend development, AOP is indispensable for implementing cross‑cutting concerns such as unified logging, permission checks, request timing, data masking, transaction control, exception alerts, and idempotency. Many developers only know the simplest @Before advice and get confused about the other four types.
1. Spring AOP Advice Types
@Before : Executes before the target method. Cannot block execution, cannot obtain return value, and does not catch exceptions. Typical uses: permission validation, input logging, parameter preprocessing.
@AfterReturning : Executes after the target method completes successfully (no exception). Can access the method’s return value. Typical uses: operation logs, response data masking, business metrics.
@AfterThrowing : Executes when the target method throws an exception. Captures the exception type and message. Typical uses: exception logging, alert notifications, error persistence.
@After : Executes after the target method finishes, regardless of success or failure (similar to a finally block). Cannot modify return value or intercept exceptions. Typical uses: resource release, ThreadLocal cleanup, request trace finalization.
@Around : Wraps the entire execution of the target method. Highest priority, can decide whether to proceed, modify arguments, change the return value, and catch exceptions. Typical uses: execution timing, rate limiting, unified response wrapping, transaction control.
Standard Execution Chain
No exception: Around (pre‑logic) → @Before → target method → @AfterReturning → @After → Around (post‑logic).
With exception: Around (pre‑logic) → @Before → target method throws → @AfterThrowing → @After → Around (exception handling).
The @Around advice can also terminate the target method execution, which no other advice can do.
2. Environment Setup
Add the core dependencies to the Maven pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AOP core dependency, must be included -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>Test Controller
@RestController
@RequestMapping("/aop")
public class AopTestController {
/** Normal business interface: no exception */
@GetMapping("/normal")
public String normalMethod(String username) {
System.out.println("===== 业务方法执行:正常业务逻辑 =====");
return "操作成功!当前操作用户:" + username;
}
/** Exception business interface: deliberately throws ArithmeticException */
@GetMapping("/error")
public String errorMethod() {
System.out.println("===== 业务方法执行:异常业务逻辑 =====");
int result = 1 / 0; // trigger divide‑by‑zero
return "操作成功";
}
}3. Aspect Implementation
Create a class LogAspect and annotate it with @Aspect and @Component. Define a pointcut that matches all controller methods under the com.demo.aop.controller package, then implement the five advice methods.
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class LogAspect {
// Unified pointcut: intercept all methods of controllers in the aop package
@Pointcut("execution(* com.demo.aop.controller.*.*(..))")
public void pointCut() {}
// ==================== 1. @Before ====================
@Before("pointCut()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("
【AOP-前置通知 @Before】");
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("执行方法:" + methodName);
System.out.println("请求入参:" + Arrays.toString(args));
}
// ==================== 2. @AfterReturning ====================
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("【AOP-返回后置通知 @AfterReturning】");
System.out.println("方法正常执行完毕,返回结果:" + result);
}
// ==================== 3. @AfterThrowing ====================
@AfterThrowing(value = "pointCut()", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
System.out.println("【AOP-异常通知 @AfterThrowing】");
System.out.println("方法执行报错,异常类型:" + ex.getClass().getSimpleName());
System.out.println("异常信息:" + ex.getMessage());
}
// ==================== 4. @After ====================
@After("pointCut()")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("【AOP-最终通知 @After】流程结束,执行资源收尾、数据清除");
System.out.println("==============================================");
}
// ==================== 5. @Around (strongest) ====================
@Around("pointCut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("【AOP-环绕通知 @Around】方法执行前置处理");
long startTime = System.currentTimeMillis();
Object result = null;
try {
result = pjp.proceed(); // core business execution
} catch (Throwable throwable) {
System.out.println("【AOP-环绕通知 @Around】捕获业务异常,统一兜底处理");
return "系统繁忙,请稍后重试!";
}
long costTime = System.currentTimeMillis() - startTime;
System.out.println("【AOP-环绕通知 @Around】方法执行完毕,耗时:" + costTime + "ms");
return result;
}
}4. Full Log Demonstration
Scenario 1: Normal request /aop/normal?username=Java干货
【AOP-环绕通知 @Around】方法执行前置处理
【AOP-前置通知 @Before】
执行方法:normalMethod
请求入参:[Java干货]
===== 业务方法执行:正常业务逻辑 =====
【AOP-返回后置通知 @AfterReturning】
方法正常执行完毕,返回结果:操作成功!当前操作用户:Java干货
【AOP-最终通知 @After】流程结束,执行资源收尾、数据清除
==============================================
【AOP-环绕通知 @Around】方法执行完毕,耗时:1msScenario 2: Exception request /aop/error
【AOP-环绕通知 @Around】方法执行前置处理
【AOP-前置通知 @Before】
执行方法:errorMethod
请求入参:[]
===== 业务方法执行:异常业务逻辑 =====
【AOP-异常通知 @AfterThrowing】
方法执行报错,异常类型:ArithmeticException
异常信息:/ by zero
【AOP-最终通知 @After】流程结束,执行资源收尾、数据清除
==============================================
【AOP-环绕通知 @Around】捕获业务异常,统一兜底处理Key Conclusions
When business code throws an exception, @AfterReturning does not run – a common pitfall for newcomers.
@After (final advice) always runs, regardless of success or failure, making it the reliable place for resource cleanup.
If @Around catches an exception, the exception is swallowed and will not reach global exception handlers.
5. Practical Tips for Each Advice
@Before (pre‑processing)
Use for validation, request logging, token parsing, and idempotency checks. It runs before the method, cannot modify the return value, and does not block execution.
Print unified request parameters and trace logs.
Perform login status, permission, and role checks.
Parse request headers, store user info in context.
Validate idempotency before proceeding.
@AfterReturning (success handling)
Triggered only on successful execution; ideal for operation logs, response data masking, and business metrics.
Persist logs for create/update/delete successes.
Apply uniform data desensitization and field filtering.
Record business success for analytics, push notifications, etc.
@AfterThrowing (exception handling)
Dedicated to business exceptions; captures error details for separate persistence and alerting.
Persist exception logs and archive error messages.
Send alerts to DingTalk or enterprise WeChat for severe errors.
Distinguish custom business exceptions from unknown system errors for differentiated handling.
@After (final cleanup)
Ensures global resource release and request trace finalization.
Clear ThreadLocal user information and request context to avoid memory leaks.
Close temporary I/O streams, database connections, and release locks.
Mark the request chain as completed, guaranteeing every request ends cleanly.
@Around (all‑powerful)
Highest‑priority advice that can control the entire business flow.
Unified execution timing, performance monitoring, and slow‑interface recording.
Blacklist interception, rate limiting, and mandatory permission enforcement (can block requests).
Global response wrapping and uniform return formatting.
Distributed transaction control and automatic cache refresh.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
