Java环绕通知案例详解与实战开发指南
目录导读
- 环绕通知是什么?为什么它是AOP的核心利器?
- 环绕通知与前置/后置通知的本质区别
- 开发环境搭建与依赖引入(含版本避坑)
- 环绕通知案例开发——5步实现方法级日志与性能监控
- 实战进阶:环绕通知中的ProceedingJoinPoint深度运用
- 高频问答:环绕通知return值与异常处理陷阱
- SEO关键词优化建议(针对程序员搜索习惯)
环绕通知是什么?为什么它是AOP的核心利器?
问题: 很多初学者问:“我已经会写@Before和@After了,为什么还要学环绕通知?”

回答: 前置通知和后置通知就像“监控摄像头”只能记录事件发生前后,但无法控制事件本身是否执行,环绕通知则像“全权限管理员”——它能够完全包裹目标方法,决定:
- 是否执行目标方法
- 执行前/后添加额外逻辑
- 修改方法参数
- 替换返回值
- 捕获并处理异常
核心优势:
环绕通知(@Around)是唯一一个可以完全接管目标方法执行过程的通知类型,它通过ProceedingJoinPoint参数获得对目标方法的控制权。
环绕通知与前置/后置通知的本质区别
| 特性 | 前置/后置通知 | 环绕通知 |
|---|---|---|
| 控制方法执行 | 否 | 是(通过调用proceed()) |
| 修改返回值 | 否 | 可以 |
| 修改参数 | 需要额外API | 直接通过ProceedingJoinPoint |
| 异常处理 | 依赖于异常通知 | 可以包裹try-catch |
| 典型场景 | 简单日志/权限校验 | 事务管理/性能监控/缓存 |
重要提醒: 在搜索引擎中,用户经常搜索“环绕通知 vs 前置通知 区别”、“Spring环绕通知什么时候用”,因此这部分内容对SEO至关重要。
开发环境搭建与依赖引入(含版本避坑)
推荐环境:
- JDK 17+
- Spring Boot 3.x(如果用2.x,AOP依赖无变化)
- IDE:IntelliJ IDEA / Eclipse
Maven核心依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
版本避坑: Spring Boot 2.5以下版本需额外引入aspectjweaver,3.x自动包含。
启动类注解:
@SpringBootApplication
@EnableAspectJAutoProxy // 启用AOP代理,Spring Boot 3.x可选但建议显式声明
public class AopApplication {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
}
}
环绕通知案例开发——5步实现方法级日志与性能监控
案例目标: 监控Service层所有方法执行时间,并输出日志格式:[执行方法名] 耗时XXXms
第1步:创建目标类(被拦截对象)
@Service
public class UserService {
public String getUserName(Long id) {
// 模拟耗时操作
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "用户_" + id;
}
}
第2步:编写环绕通知切面
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object measureMethodTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑:记录开始时间
long start = System.currentTimeMillis();
String methodName = joinPoint.getSignature().toShortString();
try {
// 核心:执行目标方法
Object result = joinPoint.proceed();
return result; // 返回原始结果
} catch (Exception e) {
// 异常时记录
System.err.println(methodName + " 执行异常: " + e.getMessage());
throw e; // 必须重新抛出,否则原始调用方收不到异常
} finally {
// 后置逻辑:计算耗时
long duration = System.currentTimeMillis() - start;
System.out.println("[" + methodName + "] 耗时: " + duration + "ms");
}
}
}
第3步:验证切面生效(测试类)
@SpringBootTest
@Slf4j
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testGetUserName() {
String name = userService.getUserName(100L);
System.out.println("返回结果: " + name);
}
}
控制台输出示例:
[UserService.getUserName(..)] 耗时: 203ms
返回结果: 用户_100
第4步:增强——记录入参和返回值
在实际项目中,往往需要记录参数,在proceed()之前获取参数:
Object[] args = joinPoint.getArgs();
System.out.println("入参: " + Arrays.toString(args));
// 可在proceed之前修改参数
// args[0] = 200L; // 若修改,需要调用proceed(args)
Object result = joinPoint.proceed(args);
第5步:处理返回值修改场景
如果环绕通知需要改变返回值(例如缓存场景):
@Around("@annotation(com.example.annotation.CacheResult)")
public Object cacheAround(ProceedingJoinPoint pjp) throws Throwable {
String cacheKey = generateCacheKey(pjp);
Object cachedValue = redisTemplate.opsForValue().get(cacheKey);
if (cachedValue != null) {
return cachedValue; // 直接返回缓存,不执行目标方法
}
Object realResult = pjp.proceed();
redisTemplate.opsForValue().set(cacheKey, realResult, 10, TimeUnit.MINUTES);
return realResult;
}
实战进阶:环绕通知中的ProceedingJoinPoint深度运用
高频问题: ProceedingJoinPoint和JoinPoint有什么区别?
回答:
JoinPoint:用于前置、后置、异常通知,不能调用proceed()ProceedingJoinPoint:仅用于环绕通知,继承自JoinPoint,新增proceed()方法
控制参数传递的三种方式:
- 默认执行:
Object result = joinPoint.proceed(); - 修改参数:
Object result = joinPoint.proceed(newArgs); - 不执行方法: 直接返回null或自定义值
实际开发陷阱:
- 必须调用proceed(),否则目标方法不会执行
- 如果多次调用proceed(),目标方法会多次执行(多用于重试机制)
- 修改参数时,新的参数数组长度必须与原参数一致
高频问答:环绕通知return值与异常处理陷阱
Q1:环绕通知里忘记return result,会发生什么?
A: 调用方会收到null,示例:
@Around("execution(* *.*(..))")
public Object wrongAround(ProceedingJoinPoint pjp) throws Throwable {
pjp.proceed();
// 缺少return,即使目标方法返回"成功",调用方得到的是null
}
正确做法: 除非故意要让方法返回null,否则必须携带return result;
Q2:环绕通知中捕获异常后需要重新抛出吗?
A: 分情况:
- 需要事务回滚: 必须重新抛出(例如
throw e;),否则Spring认为事务成功提交 - 需要屏蔽异常: 捕获后返回一个默认值(如
return "error"),此时不重新抛出 - 日志记录: 捕获后记录日志,推荐重新抛出,保持原始行为
Q3:多个环绕通知的执行顺序如何控制?
A: 使用@Order注解,数值越小越先执行外层的环绕通知,多个切面时,执行顺序类似“洋葱模型”:
通知A执行前 → 通知B执行前 → 目标方法 → 通知B执行后 → 通知A执行后
SEO优化建议(针对程序员搜索习惯)
为了让文章在搜索引擎中获得更好排名,建议注意以下自然关键词嵌入:
- 核心词:Java环绕通知、Spring AOP环绕通知、Around注解示例
- 长尾词:环绕通知proceed用法、环绕通知修改参数、环绕通知异常处理
- 对比词:前置通知 vs 环绕通知、性能监控AOP实现
文章更新建议: 每隔半年检查Spring框架版本,更新依赖和最佳实践,保持时效性。
环绕通知是Spring AOP中最强大、最灵活的通知类型,掌握它,你就能轻松实现事务管理、性能监控、缓存、日志等企业级通用功能,从本文的5步案例出发,在真实项目中反复练习,你很快就能成为AOP高手。