spring AOP 切面编程的使用
AOP切面编程的最佳应用场景
- 记录日志
- 性能监控
- 事务管理
- 处理异常
- 数据验证,验证传入参数的正确性(一般不用这个方法做,而是用拦截器)
spring提供了以下注解供开发者使用,编写AOP程序
- @Aspect 申明切面
- @Pointcut 切点,申明AOP的作用范围(如:一个类下所有方法,某个方法,带有指定注解的方法)
- @Before 前置通知
- @After 后置通知
- @Around 环绕通知
- @AfterThrowing 异常通知,当被切入点中代码执行异常时触发
实操
举个例子
- 定义
如果我希望在上面方法使用AOP,定义一个类,实现如下@RestController @RequestMapping(path = {"test"}) public class DemoController { @GetMapping(path = {"test01/{name}"}) public ResponseEntity<String> test01(@PathVariable("name") String name) { return new ResponseEntity<>(name.toUpperCase(), HttpStatus.OK); } }
@Slf4j @Aspect @Component public class AOPAction { // 定义切点,标记此切点在什么范围内此切面起作用 @Pointcut("execution(* com.train.controller.DemoController.test02(..))") public void pointcut() {} //前置通知 @Before(value = "pointcut()") public void before(JoinPoint joinPoint) { log.info("Before Method : {}...{}", joinPoint.getSignature().getName(), args[0].toString()); } // 后置通知 @After(value = "pointcut()") public void after(JoinPoint joinPoint) { log.info("After Method : {}...{}", joinPoint.getSignature().getName(), args[0].toString()); } // 环绕通知 @Around(value = "pointcut()") public Object around1(ProceedingJoinPoint joinPoint) throws Throwable { log.info("Around 1 Method before: {}...", joinPoint.getSignature().getName()); Object result = joinPoint.proceed(); log.info("Around 1 Method after: {}...", joinPoint.getSignature().getName()); return result; } }
切点执行范围
此章节介绍多种切点定义形式,开发中使用哪一种视情况而定
1.指定包下所有类的所有方法
应用在此包下所有类的所有方法上
// 包名:com.train.controller
@Pointcut("execution(* com.train.controller..*.*(..))")
public void pointcut() {}
2.指定类中所有方法
应用在指定类的所有方法上
// 全类名:com.train.controller.DemoController
@Pointcut("execution(* com.train.controller.DemoController.*(..))")
public void pointcut() {}
3.指定某个类中某个方法
应用在指定类的指定方法上,而此类中的其他方法不会被应用
// 全类名:com.train.controller.DemoController
@Pointcut("execution(* com.train.controller.DemoController.test01(..))")
public void pointcut() {}
4.1 按参数数量指定
AOP切入点表达式规范中,用*
表示一个参数,用..
表示无固定个数参数
可以这么理解:把*
理解成通配符,且仅匹配一个。
// 仅作用在com.train.controller包下所有类中仅有2个参数的方法上
@Pointcut(value = "execution(* com.train.controller..*.*(*,*))")
public void pointcut02(){}
// 仅作用在com.train.controller包下所有类中仅有5个参数的方法上
@Pointcut(value = "execution(* com.train.controller..*.*(*,*,*,*,*))")
public void pointcut02(){}
4.2 按参数注解指定
⚠️ 研究中,未完成
5.加有指定注解的方法
仅应用在使用了注解的方法上,可以使用自定义注解,也可以使用框架提供注解。此形式最灵活,推荐开发中使用
// 自定义注解
public @interface LogPointCut {}
// 加注解的方法
@GetMapping(path = {"test/{name}"})
@LogPointCut
public ResponseEntity<String> test(@PathVariable("name") String name) {
return new ResponseEntity<>(name.toUpperCase(), HttpStatus.OK);
}
// 定义
@Pointcut("@annotation(com.train.annotation.LogPointCut)")
public void pointcut() {
}
6.组合条件【难点】
使用A符合 Spring AOP 的切入点表达式规范组合多个条件,组合条件最好用&&
连接各个条件。用+
匹配接口。用*
匹配一个。用..
匹配无固定数
举例
// 仅作用在com.train.controller包下所有类中所有标记了@LogPointCut注解的方法上
@Pointcut(value = "execution(* com.train.controller..*.*(..)) && @annotation(com.train.spr.annotation.LogPointCut)")
public void pointcut02(){}
// 仅作用在com.train.spr.controller包下所有实现了MyInterface接口的类的方法上
@Pointcut(value = "execution(* (com.train.controller..* && com.train.spr.interfaces.MyInterface+).*(..))")
public void pointcut02(){}
// 仅作用在com.train.spr.controller包下所有实现了MyInterface接口和MyInterface2接口的类的方法上
@Pointcut(value = "execution(* (com.train.controller..* " +
"&& com.train.spr.interfaces.MyInterface+ " +
"&& com.train.spr.interfaces.MyInterface2+ ).*(..))")
public void pointcut02(){}
当然!也可以组合多个已定义的条件作为一个条件,例如
import org.aspectj.lang.annotation.Pointcut;
// 仅作用在com.train.spr.controller包下类的方法上
@Pointcut(value = "execution(* com.train.controller..*.*(..))")
public void pointcut01() {}
// 仅作用在实现了MyInterface接口的类的方法上
@Pointcut(value = "execution(* com.train.spr.interfaces.MyInterface+.*(..))")
public void pointcut02() {}
// 组合上述两个切入点的条件
@Pointcut(value = "pointcut02() && pointcut01()")
public void pointcut03() {}
AOP切入点表达式规范请查阅官网
获取参数
AOP的方法可以获取被切入方法的参数,但是修改这些参数并不会在被切入方法中生效。举个例子,方法A有一个前置通知方法B,且方法B获取方法A的参数,即便方法B中修改了参数值,方法A获取到的参数仍然不会改变,原来是怎样就是怎样
获取参数这块注意3点就够了。
- 前置通知/后置通知的参数是
org.aspectj.lang.JoinPoint
- 环绕通知的参数是
org.aspectj.lang.ProceedingJoinPoint
- 被切入点的参数信息都被封装到
JoinPoint
或ProceedingJoinPoint
中,直接从中获取即可【注意类型】
特殊的切入方法——异常通知
研究中,未完成
本文来自博客园,作者:勤匠,转载请注明原文链接:https://www.cnblogs.com/JarryShu/articles/18513814
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现