news 2026/4/16 11:49:58

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/4/3 4:51:30

导师严选9个AI论文网站,助你轻松搞定本科生毕业论文!

导师严选9个AI论文网站&#xff0c;助你轻松搞定本科生毕业论文&#xff01; AI 工具助你轻松应对论文写作难题 在当今信息化时代&#xff0c;AI 工具已经渗透到各个领域&#xff0c;学术写作也不例外。对于本科生而言&#xff0c;毕业论文的撰写是一项既重要又复杂的任务&…

作者头像 李华
网站建设 2026/4/11 16:14:40

基于Qt的轻量级Ribbon控件:打造Office样式UI

基于Qt的轻量级的Ribbon控件(Office样式UI),界面截图&#xff1a; 它支持4种目前常见的ribbon样式在线切换 包括2种office模式&#xff0c;office模式是最常见的ribbon模式了&#xff0c;就是我们经常看到的word模式&#xff0c;office模式的tab和标题栏占用位置较多。 另两种参…

作者头像 李华
网站建设 2026/4/15 14:41:30

基于java+ vue银行柜台管理系统(源码+数据库+文档)

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

作者头像 李华
网站建设 2026/4/16 13:44:05

M2FP模型在AR试鞋中的人体足部分割应用

M2FP模型在AR试鞋中的人体足部分割应用 &#x1f9e9; M2FP 多人人体解析服务&#xff1a;精准分割&#xff0c;赋能AR交互 随着增强现实&#xff08;AR&#xff09;技术在电商、虚拟穿搭等场景的深入应用&#xff0c;高精度的人体部位语义分割成为实现沉浸式体验的核心基础。尤…

作者头像 李华
网站建设 2026/4/15 13:17:28

M2FP模型在数字艺术中的应用:创意人体分割作品

M2FP模型在数字艺术中的应用&#xff1a;创意人体分割作品 &#x1f3a8; 数字艺术新范式&#xff1a;从人体解析到视觉创作 在当代数字艺术创作中&#xff0c;精准的人体结构理解已成为连接算法与美学的关键桥梁。传统图像处理手段往往依赖手动标注或粗粒度的轮廓提取&#xf…

作者头像 李华
网站建设 2026/4/13 8:53:55

SQL零基础到精通:用SQLYNX轻松学习数据库查询

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个交互式SQL学习平台&#xff0c;利用SQLYNX技术提供渐进式学习体验。包含&#xff1a;基础知识讲解、交互式练习(带即时反馈)、可视化查询构建器、学习进度跟踪和个性化推荐…

作者头像 李华