news 2026/2/26 10:53:55

详细解释动态切入点(DynamicMethodMatcherPointcut)在Spring AOP中的完整处理流程。

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
详细解释动态切入点(DynamicMethodMatcherPointcut)在Spring AOP中的完整处理流程。

核心问题澄清
对于动态切入点,Spring仍然会根据静态匹配的结果创建代理,但代理内部会包含额外的逻辑来处理动态匹配。

让我通过源码级别的解释来详细说明:

Spring AOP 的代理创建流程

  1. 代理创建过程概览
// Spring内部简化逻辑publicclassDefaultAdvisorAutoProxyCreator{publicObjectwrapIfNecessary(Objectbean,StringbeanName){// 1. 找到所有适用的AdvisorList<Advisor>advisors=findEligibleAdvisors(beanClass,beanName);// 2. 为bean创建代理if(!advisors.isEmpty()){returncreateProxy(beanClass,beanName,advisors,targetSource);}returnbean;}}
  1. 静态匹配在代理创建中的作用
// Spring AOP 内部的处理逻辑publicclassAopUtils{publicstaticList<Advisor>findAdvisorsThatCanApply(List<Advisor>advisors,Class<?>clazz){List<Advisor>eligibleAdvisors=newArrayList<>();for(Advisoradvisor:advisors){if(canApply(advisor,clazz)){// 这里调用静态匹配eligibleAdvisors.add(advisor);}}returneligibleAdvisors;}publicstaticbooleancanApply(Advisoradvisor,Class<?>targetClass){Pointcutpc=advisor.getPointcut();// 首先进行静态匹配if(!pc.getClassFilter().matches(targetClass)){returnfalse;}// 获取目标类的所有方法Method[]methods=targetClass.getMethods();for(Methodmethod:methods){// 这里调用静态的matches(Method, Class<?>)方法if(pc.getMethodMatcher().matches(method,targetClass)){returntrue;// 只要有一个方法匹配,就为该类创建代理}}returnfalse;}}
  1. 动态匹配的运行时处理
    当使用DynamicMethodMatcherPointcut时,Spring会创建特殊的MethodInterceptor来支持动态检查:
// Spring内部的动态代理调用链publicclassReflectiveMethodInvocationimplementsMethodInvocation{privateList<Object>interceptorsAndDynamicMethodMatchers;publicObjectproceed()throwsThrowable{// 如果还有拦截器if(this.currentInterceptorIndex==this.interceptorsAndDynamicMethodMatchers.size()-1){// 调用原始方法returninvokeJoinpoint();}ObjectinterceptorOrInterceptionAdvice=this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if(interceptorOrInterceptionAdviceinstanceofInterceptorAndDynamicMethodMatcher){// 这是动态切入点!InterceptorAndDynamicMethodMatcherdm=(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;// 执行动态匹配if(dm.methodMatcher.matches(this.method,this.targetClass,this.arguments)){returndm.interceptor.invoke(this);}else{// 动态匹配失败,跳过此通知returnproceed();}}else{// 静态切入点,直接执行return((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);}}}

具体示例分析
让我用一个具体的例子来说明:

  1. 示例代码
@ServicepublicclassDataService{publicStringfindById(Longid){System.out.println("执行 findById: "+id);return"Data-"+id;}publicStringfindByName(Stringname){System.out.println("执行 findByName: "+name);return"Data-"+name;}publicvoidsave(Stringdata){System.out.println("执行 save: "+data);}}@ComponentpublicclassAuditPointcutextendsDynamicMethodMatcherPointcut{@Overridepublicbooleanmatches(Methodmethod,Class<?>targetClass){System.out.println("静态匹配检查: "+method.getName());// 只匹配"find"开头的方法returnmethod.getName().startsWith("find");}@Overridepublicbooleanmatches(Methodmethod,Class<?>targetClass,Object...args){System.out.println("动态匹配检查: "+method.getName()+", 参数: "+Arrays.toString(args));// 只对ID大于100的记录进行审计if(method.getName().equals("findById")&&args.length>0){Longid=(Long)args[0];returnid>100;}returnfalse;}}
  1. Spring内部的实际处理流程
// Spring创建代理时的伪代码表示publicclassSpringProxyCreator{publicObjectcreateProxy(Class<?>targetClass,Objecttarget){// 1. 检查AuditPointcut的静态匹配// - 检查findById方法:静态匹配通过// - 检查findByName方法:静态匹配通过// - 检查save方法:静态匹配失败// 2. 为DataService创建代理// 代理会拦截findById和findByName方法returnProxy.newProxyInstance(targetClass.getClassLoader(),targetClass.getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args){// 对于动态切入点,这里会包含额外的检查逻辑if(method.getName().startsWith("find")){// 这是动态切入点的方法returnhandleDynamicPointcut(method,args);}else{// 其他方法直接调用returnmethod.invoke(target,args);}}privateObjecthandleDynamicPointcut(Methodmethod,Object[]args){// 先检查动态匹配if(auditPointcut.matches(method,targetClass,args)){// 执行审计通知System.out.println("【审计开始】");Objectresult=method.invoke(target,args);System.out.println("【审计结束】");returnresult;}else{// 动态匹配失败,直接执行原方法returnmethod.invoke(target,args);}}});}}
  1. 运行时的实际调用流程
publicclassTest{publicstaticvoidmain(String[]args){ApplicationContextcontext=...;DataServicedataService=context.getBean(DataService.class);// 情况1: findById(50) - 静态匹配通过,动态匹配失败// 代理会拦截,执行动态检查,不执行通知dataService.findById(50L);// 情况2: findById(150) - 静态匹配通过,动态匹配通过// 代理会拦截,执行动态检查,执行通知dataService.findById(150L);// 情况3: findByName("test") - 静态匹配通过,动态匹配失败// 代理会拦截,执行动态检查,不执行通知dataService.findByName("test");// 情况4: save("data") - 静态匹配失败// 代理不会拦截,直接调用原方法dataService.save("data");}}

内存和性能影响

  1. 代理对象的内存布局
// 对于动态切入点的代理对象publicclassDynamicPointcutProxy{// 包含:// 1. 目标对象的引用// 2. 动态切入点的引用// 3. 通知(Advice)的引用// 4. 拦截器链(包含动态匹配逻辑)// 比普通代理多存储的信息:// - 动态匹配的检查逻辑// - 运行时参数的处理逻辑}
  1. 性能对比
// 性能测试示例@SpringBootTestclassPerformanceTest{@TestvoidtestPerformance(){longstart,end;// 测试动态切入点start=System.nanoTime();for(inti=0;i<1000000;i++){dataService.findById((long)i);// 每次调用都执行动态匹配}end=System.nanoTime();System.out.println("动态切入点耗时: "+(end-start)/1000000+" ms");// 测试静态切入点(对比)start=System.nanoTime();for(inti=0;i<1000000;i++){dataService.save("data");// 直接调用,无动态检查}end=System.nanoTime();System.out.println("静态切入点耗时: "+(end-start)/1000000+" ms");}}

核心要点总结

  1. 动态切入点的代理创建规则
    基于静态匹配创建代理:只要matches(Method, Class<?>)返回true的方法,都会被代理

代理包含动态检查逻辑:代理对象内部会包含对动态匹配的检查

运行时决定是否执行通知:每次调用时执行动态匹配,决定是否触发通知

  1. 代理的结构差异
// 静态切入点代理(StaticMethodMatcherPointcut)静态切入点代理{目标对象引用 拦截器链(直接执行通知)}// 动态切入点代理(DynamicMethodMatcherPointcut)动态切入点代理{目标对象引用 动态切入点引用 拦截器链(包含动态检查逻辑+通知)}
  1. 实际代码执行路径
// 调用动态切入点方法的执行路径1.代理对象.invoke()2.检查是否是动态切入点方法(静态匹配已通过) ↓3.执行动态匹配:matches(method,targetClass,args)4.如果匹配通过 → 执行通知 → 执行原方法 ↓5.如果匹配失败 → 直接执行原方法
  1. 重要设计原则
    代理最小化原则:Spring只代理需要代理的方法,而不是整个类

性能优化:通过静态匹配减少代理创建的数量

运行时灵活性:通过动态匹配提供基于参数的运行时决策

  1. 验证方法
    您可以通过以下方式验证:
@SpringBootTestclassVerifyTest{@TestvoidverifyProxyCreation(){DataServicedataService=context.getBean(DataService.class);// 验证代理类型System.out.println("Bean类型: "+dataService.getClass().getName());// 输出可能: com.sun.proxy.$ProxyXX 或 DataService$$EnhancerBySpringCGLIB$$// 验证方法是否被代理System.out.println("findById 方法是否被代理: "+AopUtils.isAopProxy(dataService)&&AopUtils.isCglibProxy(dataService));// 查看代理的拦截器if(AopUtils.isAopProxy(dataService)){Advisedadvised=(Advised)dataService;System.out.println("拦截器数量: "+advised.getAdvisors().length);for(Advisoradvisor:advised.getAdvisors()){System.out.println("拦截器: "+advisor.getClass().getName());}}}}

总结回答您的问题
Q: 在容器初始化节点会根据静态Pointcut创建被代理的代理对象,那动态Pointcut?

A: 动态切入点(DynamicMethodMatcherPointcut)在容器初始化时:

同样会根据静态匹配结果创建代理:Spring调用matches(Method, Class<?>),为返回true的方法创建代理

代理内部包含动态检查逻辑:创建的代理对象会包含额外的代码来处理动态匹配

运行时执行动态决策:每次方法调用时,代理会执行matches(Method, Class<?>, Object...)来决定是否触发通知

不是两个独立的代理:不存在"静态代理"和"动态代理"两个对象,而是一个代理对象包含了动态匹配的能力

简单来说:动态切入点也是基于静态匹配创建代理的,但代理内部多了运行时参数检查的逻辑

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

为什么推荐M2FP给中小企业?零成本+零运维即可上线

为什么推荐M2FP给中小企业&#xff1f;零成本零运维即可上线 在当前AI技术快速普及的背景下&#xff0c;越来越多的中小企业开始探索计算机视觉能力的应用场景——从智能零售试衣到健身动作分析&#xff0c;再到虚拟形象生成。然而&#xff0c;高昂的算力成本、复杂的模型部署流…

作者头像 李华
网站建设 2026/2/26 14:16:01

阿里团队突破:大模型训练稳定性难题破解

这项由阿里巴巴Qwen团队的郑楚杰、邓凯、于博文等研究者完成的研究发表于2025年12月&#xff0c;论文编号为arXiv:2512.01374v1。有兴趣深入了解的读者可以通过该编号查询完整论文。想象一下教小孩学数学的场景&#xff1a;你给孩子出题&#xff0c;孩子答题&#xff0c;然后你…

作者头像 李华
网站建设 2026/2/26 15:23:28

人体解析入门指南:M2FP提供完整API文档与调用示例

人体解析入门指南&#xff1a;M2FP提供完整API文档与调用示例 &#x1f4d6; 项目简介&#xff1a;M2FP 多人人体解析服务 在计算机视觉领域&#xff0c;人体解析&#xff08;Human Parsing&#xff09; 是一项关键的细粒度语义分割任务&#xff0c;旨在将图像中的人体分解为多…

作者头像 李华
网站建设 2026/2/24 14:08:53

三维地质建模数据处理高级实践技术应用

三维地质建模计算在地质工程、地球物理、矿产勘查等领域获得了广泛的应用&#xff0c;常用软件包括GOCAD、Surpac、XModel、DMine等。通过三维地质建模&#xff0c;既可以表达空间几何对象&#xff0c;也可以表现空间属性分布&#xff0c;进而实现地下三维空间可视化、地质解释…

作者头像 李华
网站建设 2026/2/26 12:05:16

M2FP模型在体育训练中的应用:运动员动作分析

M2FP模型在体育训练中的应用&#xff1a;运动员动作分析 &#x1f9e9; M2FP 多人人体解析服务 在现代体育科学中&#xff0c;精准的动作分析已成为提升运动员表现、预防运动损伤的核心手段。传统的动作捕捉系统依赖昂贵的传感器设备和复杂的布点环境&#xff0c;难以普及到基层…

作者头像 李华