Spring框架中的AOP技术(面向切面编程)是一种可以在不修改源代码的情况下,对系统进行横向切割的技术。其中,afterreturning是其中一个重要的切面类型,它允许在方法正常返回之后,在方法返回上下文中引入一个切面。下面从多个角度深入理解afterreturning中的细节和用法。
一、用法概述
正如其名,afterreturning是在方法正常返回之后执行的增强类型。它拥有三个最常用的属性:
- value:指定切点表达式
- returning:指定一个用于接受方法返回值的参数名
- argNames:指定方法参数名,逗号分隔
下面是一个afterreturning的示例:
@Aspect @Component public class LogAspect { @AfterReturning(value = "execution(* com.example.service.UserService.*(..))", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); System.out.println("AfterReturning: " + className + " " + methodName + " returning " + result); } }
以上代码实现了在UserService中方法正常返回之后输出方法名称和返回结果的功能。这里需要注意的是,returning属性指定的参数名称必须要与方法签名中的参数名完全一致,否则无法正常接收返回值。
二、如何获取方法参数
有时候我们需要获取方法的各个参数来进一步处理,对于这个需求,afterreturning也可以支持。需要借助两个注解:@AfterReturning和@AfterReturning arg。示例代码如下:
@AfterReturning(value = "execution(* com.example.service.UserService.*(..))", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); System.out.println("AfterReturning: " + className + " " + methodName + " returning " + result); Object[] args = joinPoint.getArgs(); for (Object obj : args) { if (obj instanceof HttpServletRequest) { System.out.println("HttpServletRequest: " + obj); } } } @AfterReturning(argNames = "request,result", value = "execution(* com.example.controller.UserController.getAll(..))") public void logAfterReturning(JoinPoint joinPoint, HttpServletRequest request, Object result) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); System.out.println("AfterReturning: " + className + " " + methodName + " returning " + result); }
以上代码演示了如何获取方法中的HttpServletRequest参数。需要注意的是,在后面的示例代码中,argNames属性指定了方法签名中的参数名,这样我们就能够通过参数名将参数一一对应起来,方便我们在增强中处理它们。
三、afterreturning的用途
在实际应用中,afterreturning有很多种用途。下面我们列出了三种最常见的:
1、日志输出
以前面的日志输出示例为例,afterreturning是用来实现日志输出功能的。在方法正常返回之后,通过增强完成打印日志的功能。
2、返回值处理
有时候,我们需要对方法返回值进行一些处理。例如,添加一些统一的前缀或后缀等等。下面是一个示例代码:
@AfterReturning(value = "execution(* com.example.service.UserService.*(..))", returning = "result") public void addPrefixToResult(JoinPoint joinPoint, Object result) { String className = joinPoint.getTarget().getClass().getSimpleName(); if (result instanceof String) { result = "[Prefix]" + result + "[Suffix]"; } System.out.println("AfterReturning: " + className + " returning " + result); }
以上代码实现了给返回的String类型结果添加Prefix和Suffix的功能。需要注意的是,result是一个Object类型,需要判断其是否为String类型,然后再进行增强操作。
3、缓存实现
通过缓存实现数据的快速读取是很多高并发场景下的解决方案。在Spring中,afterreturning可以用于缓存实现,示例代码如下:
@AfterReturning(value = "execution(* com.example.service.UserService.getUserById(String)) && args(id)", returning = "result") public void cacheUserById(String id, UserDTO userDTO) { redisTemplate.opsForValue().set(id, userDTO); }
以上代码实现了通过ID缓存UserDTO的功能,在方法正常返回之后将其缓存到Redis中,下次查询相同的ID时可以避免数据库查询的开销。
四、总结
本文从用法概述、如何获取方法参数、afterreturning的用途三个方面深入理解了afterreturning的细节和用法。通过灵活使用这个切面类型,我们可以避免在修改源代码的情况下进行代码横向切割和重构。