news 2026/4/3 4:14:41

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/3/27 18:43:26

零售场景AI应用:M2FP解析顾客身形,驱动个性化推荐引擎

零售场景AI应用&#xff1a;M2FP解析顾客身形&#xff0c;驱动个性化推荐引擎 在智能零售的演进中&#xff0c;精准理解用户体态特征正成为提升购物体验的关键突破口。传统推荐系统多依赖历史行为数据或静态标签&#xff0c;难以捕捉消费者当下的穿搭意图与身形适配需求。而基于…

作者头像 李华
网站建设 2026/4/1 8:02:58

中小团队如何高效构建“价值型IP”?知识付费的下一个机会点

当流量红利逐渐平缓&#xff0c;大规模、粗放式的矩阵运营模式面临成本与效率的双重挑战。知识付费领域正在显露出一个清晰的趋势&#xff1a;基于中小型精锐团队的、深度价值驱动的IP模式&#xff0c;正成为更具韧性、更可持续的发展路径。 这并非退而求其次&#xff0c;而是在…

作者头像 李华
网站建设 2026/3/27 6:49:21

AI 英语学习 APP 的外包流程

AI 英语学习 APP 的外包流程比传统教育软件更复杂&#xff0c;因为它不仅涉及前端交互&#xff0c;还需要深度集成 LLM&#xff08;大语言模型&#xff09;、语音识别&#xff08;ASR&#xff09; 以及个性化推荐算法。以下是 AI 英语学习 APP 外包开发的标准流程&#xff1a;第…

作者头像 李华
网站建设 2026/4/1 17:18:25

M2FP模型在智能家居中的人体感知应用

M2FP模型在智能家居中的人体感知应用 &#x1f4cc; 引言&#xff1a;从智能安防到精细化交互的人体感知需求 随着智能家居系统向更高层次的环境理解与人机交互演进&#xff0c;传统“有人/无人”的粗粒度检测已无法满足场景智能化的需求。现代家庭场景需要更精细的人体状态感知…

作者头像 李华
网站建设 2026/4/2 19:19:26

基于java + vue美食分享管理系统(源码+数据库+文档)

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

作者头像 李华