Java环绕通知案例怎么开发

wen java案例 26

Java环绕通知案例详解与实战开发指南

目录导读

  • 环绕通知是什么?为什么它是AOP的核心利器?
  • 环绕通知与前置/后置通知的本质区别
  • 开发环境搭建与依赖引入(含版本避坑)
  • 环绕通知案例开发——5步实现方法级日志与性能监控
  • 实战进阶:环绕通知中的ProceedingJoinPoint深度运用
  • 高频问答:环绕通知return值与异常处理陷阱
  • SEO关键词优化建议(针对程序员搜索习惯)

环绕通知是什么?为什么它是AOP的核心利器?

问题: 很多初学者问:“我已经会写@Before和@After了,为什么还要学环绕通知?”

Java环绕通知案例怎么开发

回答: 前置通知和后置通知就像“监控摄像头”只能记录事件发生前后,但无法控制事件本身是否执行,环绕通知则像“全权限管理员”——它能够完全包裹目标方法,决定:

  • 是否执行目标方法
  • 执行前/后添加额外逻辑
  • 修改方法参数
  • 替换返回值
  • 捕获并处理异常

核心优势:
环绕通知(@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深度运用

高频问题: ProceedingJoinPointJoinPoint有什么区别?
回答:

  • JoinPoint:用于前置、后置、异常通知,不能调用proceed()
  • ProceedingJoinPoint:仅用于环绕通知,继承自JoinPoint,新增proceed()方法

控制参数传递的三种方式:

  1. 默认执行: Object result = joinPoint.proceed();
  2. 修改参数: Object result = joinPoint.proceed(newArgs);
  3. 不执行方法: 直接返回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高手。

抱歉,评论功能暂时关闭!