news 2026/2/18 0:15:28

Spring Boot实现DynamicMethodMatcherPointcut示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot实现DynamicMethodMatcherPointcut示例
  1. Maven 依赖 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>dynamic-pointcut-demo</artifactId><version>1.0.0</version><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
  1. 主应用类
packagecom.example;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.ConfigurableApplicationContext;importorg.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootApplication@EnableAspectJAutoProxy(proxyTargetClass=true)publicclassDynamicPointcutApplication{publicstaticvoidmain(String[]args){ConfigurableApplicationContextcontext=SpringApplication.run(DynamicPointcutApplication.class,args);// 测试动态切入点UserServiceuserService=context.getBean(UserService.class);OrderServiceorderService=context.getBean(OrderService.class);System.out.println("=== 测试开始 ===");// 测试1: 匹配方法userService.getUserById(123L);userService.getUserById(456L);// 测试2: 不匹配的方法userService.getAllUsers();// 测试3: 其他服务的方法orderService.createOrder("product1",2);orderService.cancelOrder(789L);System.out.println("=== 测试结束 ===");context.close();}}
  1. 自定义 DynamicMethodMatcherPointcut
packagecom.example.aop;importorg.springframework.aop.MethodMatcher;importorg.springframework.aop.support.DynamicMethodMatcherPointcut;importorg.springframework.stereotype.Component;importjava.lang.reflect.Method;/** * 自定义动态方法匹配切入点 * 动态匹配:每次方法调用时都会执行匹配检查 * 可以根据运行时参数决定是否应用通知 */@ComponentpublicclassUserIdAuditPointcutextendsDynamicMethodMatcherPointcut{/** * 静态匹配检查 - 在代理创建时执行一次 * 可以在这里进行快速筛选,减少动态检查的开销 */@Overridepublicbooleanmatches(Methodmethod,Class<?>targetClass){// 只匹配UserService类if(!targetClass.getName().contains("UserService")){returnfalse;}// 只匹配方法名以"getUser"开头的方法StringmethodName=method.getName();returnmethodName.startsWith("getUser");}/** * 动态匹配检查 - 每次方法调用时执行 * 可以根据方法参数进行动态判断 */@Overridepublicbooleanmatches(Methodmethod,Class<?>targetClass,Object...args){// 如果静态匹配不通过,直接返回falseif(!matches(method,targetClass)){returnfalse;}// 检查参数if(args!=null&&args.length>0){ObjectfirstArg=args[0];// 只对userId为奇数的请求进行审计if(firstArginstanceofLong){LonguserId=(Long)firstArg;returnuserId%2!=0;// 只审计奇数ID}}returnfalse;}/** * 这个方法来自MethodMatcher接口 * 对于DynamicMethodMatcherPointcut,必须返回true * 表示这是一个动态匹配器 */@OverridepublicbooleanisRuntime(){returntrue;// 表明这是动态切入点}}
  1. 定义通知 (Advice)
packagecom.example.aop;importorg.aopalliance.intercept.MethodInterceptor;importorg.aopalliance.intercept.MethodInvocation;importorg.springframework.stereotype.Component;importjava.util.Arrays;/** * 审计通知 - 在方法执行前后进行审计 */@ComponentpublicclassAuditAdviceimplementsMethodInterceptor{@OverridepublicObjectinvoke(MethodInvocationinvocation)throwsThrowable{StringmethodName=invocation.getMethod().getName();Object[]args=invocation.getArguments();// 前置审计System.out.println("【审计开始】方法: "+methodName+", 参数: "+Arrays.toString(args));longstartTime=System.currentTimeMillis();try{// 执行原方法Objectresult=invocation.proceed();// 后置审计longendTime=System.currentTimeMillis();System.out.println("【审计成功】方法: "+methodName+", 执行时间: "+(endTime-startTime)+"ms"+", 结果: "+result);returnresult;}catch(Exceptione){// 异常审计System.out.println("【审计失败】方法: "+methodName+", 异常: "+e.getMessage());throwe;}}}
  1. 配置 AOP
packagecom.example.config;importcom.example.aop.AuditAdvice;importcom.example.aop.UserIdAuditPointcut;importorg.springframework.aop.Advisor;importorg.springframework.aop.support.DefaultPointcutAdvisor;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassAopConfig{@BeanpublicAdvisoruserIdAuditAdvisor(UserIdAuditPointcutpointcut,AuditAdviceadvice){// 将切入点与通知组合成AdvisorreturnnewDefaultPointcutAdvisor(pointcut,advice);}}
  1. 业务服务类
packagecom.example.service;importorg.springframework.stereotype.Service;importjava.util.Arrays;importjava.util.List;@ServicepublicclassUserService{/** * 这个方法会被动态切入点匹配 * 只有当userId为奇数时才会触发审计 */publicStringgetUserById(LonguserId){System.out.println("执行 getUserById, userId: "+userId);return"User-"+userId;}/** * 这个方法会被静态匹配过滤掉(不以getUser开头) */publicList<String>getAllUsers(){System.out.println("执行 getAllUsers");returnArrays.asList("User-1","User-2","User-3");}/** * 这个方法会被静态匹配到,但动态匹配可能被过滤 */publicStringgetUserByName(Stringname){System.out.println("执行 getUserByName, name: "+name);return"User: "+name;}}```javapackagecom.example.service;importorg.springframework.stereotype.Service;@ServicepublicclassOrderService{publicStringcreateOrder(Stringproduct,intquantity){System.out.println("创建订单: "+product+", 数量: "+quantity);return"Order-"+System.currentTimeMillis();}publicbooleancancelOrder(LongorderId){System.out.println("取消订单: "+orderId);returntrue;}}
  1. 测试控制器 (可选,用于Web测试)
packagecom.example.controller;importcom.example.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublicclassTestController{@AutowiredprivateUserServiceuserService;@GetMapping("/user/{id}")publicStringgetUser(@PathVariableLongid){returnuserService.getUserById(id);}@GetMapping("/users")publicStringgetAllUsers(){returnuserService.getAllUsers().toString();}}
  1. 测试类
packagecom.example;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importcom.example.service.UserService;@SpringBootTestclassDynamicPointcutApplicationTests{@AutowiredprivateUserServiceuserService;@TestvoidtestDynamicPointcut(){System.out.println("=== 测试动态切入点 ===");// 测试1: userId为123(奇数)- 应该触发审计System.out.println("\n测试1 - 奇数ID (应该触发审计):");userService.getUserById(123L);// 测试2: userId为456(偶数)- 不应该触发审计System.out.println("\n测试2 - 偶数ID (不应该触发审计):");userService.getUserById(456L);// 测试3: getAllUsers - 不应该触发审计System.out.println("\n测试3 - getAllUsers (不应该触发审计):");userService.getAllUsers();// 测试4: getUserByName - 参数不是Long,静态匹配但动态不匹配System.out.println("\n测试4 - getUserByName (不应该触发审计):");userService.getUserByName("John");}}

运行结果示例

=== 测试开始 === 执行 getAllUsers 【审计开始】方法: getUserById, 参数: [123] 执行 getUserById, userId: 123 【审计成功】方法: getUserById, 执行时间: 2ms, 结果: User-123 执行 getUserById, userId: 456 执行 getAllUsers 创建订单: product1, 数量: 2 取消订单: 789 === 测试结束 ===

关键点说明
动态匹配 vs 静态匹配:

matches(Method, Class<?>):静态匹配,在代理创建时执行一次

matches(Method, Class<?>, Object...):动态匹配,每次方法调用时执行

性能考虑:

动态匹配有性能开销,因为每次方法调用都需要检查

应该先进行静态匹配过滤,减少动态匹配的调用次数

使用场景:

需要根据运行时参数决定是否应用通知

例如:只审计特定参数值的调用、参数验证等

配置要点:

isRuntime()必须返回true

需要通过DefaultPointcutAdvisor将切入点和通知组合

这个示例展示了如何创建和使用DynamicMethodMatcherPointcut来实现基于方法参数的动态AOP拦截。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/8 10:35:56

Vue2 + Cesium 3DTiles 完整实践:从环境配置到避坑指南

前言 在 Vue 项目中集成 Cesium 加载 3DTiles 模型(如倾斜摄影、BIM 模型)是地理信息可视化领域的常见需求,但过程中容易遇到模块化冲突、资源加载失败、事件总线报错等问题。本文基于 Vue2 + Vue CLI 5 环境,从基础配置、核心功能实现到常见报错修复,提供一套完整可运行…

作者头像 李华
网站建设 2026/2/15 9:17:29

上海AI实验室突破:AI实现高效思考模式告别冗余计算

这项由上海AI实验室的刘俊楠、刘宏伟、张松阳和陈恺团队完成的研究发表于2025年12月&#xff0c;论文编号为arXiv:2512.01925v1。研究团队还包括来自莫纳什大学的成员&#xff0c;感兴趣的读者可以通过该编号查询完整论文。当我们遇到数学难题时&#xff0c;有些同学能够直接抓…

作者头像 李华
网站建设 2026/2/12 11:58:13

MIT突破:机器人实现人类般流畅反应能力

这项由麻省理工学院&#xff08;MIT&#xff09;联合NVIDIA、清华大学、加州大学伯克利分校、加州大学圣地亚哥分校和加州理工学院的研究团队共同完成的研究发表于2025年11月30日的arXiv预印本平台&#xff0c;论文编号为arXiv:2512.01031v1。有兴趣深入了解的读者可以通过该编…

作者头像 李华
网站建设 2026/2/11 13:37:08

基于java+ vue学生求职就业系统(源码+数据库+文档)

学生求职就业 目录 基于springboot vue学生求职就业系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 基于springboot vue学生求职就业系统 一、前言 博主介绍&…

作者头像 李华
网站建设 2026/2/17 7:15:51

基于springboot + vue学生求职就业系统(源码+数据库+文档)

学生求职就业 目录 基于springboot vue学生求职就业系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 基于springboot vue学生求职就业系统 一、前言 博主介绍&…

作者头像 李华
网站建设 2026/2/7 0:31:51

M2FP模型在数字艺术创作中的创新应用

M2FP模型在数字艺术创作中的创新应用 &#x1f3a8; 数字艺术新引擎&#xff1a;M2FP多人人体解析服务的崛起 随着AI技术在创意领域的不断渗透&#xff0c;精准的人体结构理解已成为数字艺术生成、虚拟试衣、角色动画等应用的核心基础。传统图像分割方法在处理多人场景时常常面…

作者头像 李华