MiniSpring框架学习笔记-Pointcut:如何批量匹配代理方法?
MiniSpring框架学习笔记-Pointcut如何批量匹配代理方法19. Pointcut如何批量匹配代理方法一、先看最终效果二、先分清 Advice、Pointcut 和 Advisor三、定义 Pointcut 和 MethodMatcher四、按方法名实现切点五、用 PointcutAdvisor 组合切点和通知六、ProxyFactoryBean 要能识别 Advisor七、动态代理根据切点决定是否增强八、验证匹配结果小结教程https://github.com/YaleGuo/minis极客时间手把手带你写一个 MiniSpring19. Pointcut如何批量匹配代理方法上一节已经把增强逻辑从动态代理中抽成了MethodInterceptor不过哪些方法需要增强仍然写死在代理类里if(doAction.equals(method.getName())interceptor!null){Class?targetClassthis.targetnull?null:this.target.getClass();MethodInvocationinvocationnewReflectiveMethodInvocation(proxy,this.target,method,args,targetClass);returninterceptor.invoke(invocation);}这样只能增强doAction()。如果又增加了doSomething()、doSave()等方法就要继续修改代理类。这显然不是一个可扩展的做法。本节要引入Pointcut切点把“哪些方法需要增强”也变成独立配置。先注意一个容易混淆的地方JDK 生成的代理对象仍然代理接口中的所有方法Pointcut决定的是“哪个方法调用需要执行增强”不是“只为哪些方法生成代理”。一、先看最终效果项目中的IAction现在有两个业务方法packagecom.chenhai.test;publicinterfaceIAction{voiddoAction();voiddoSomething();}调用方还是照常从容器中获取代理对象IActionaction(IAction)context.getBean(action);action.doAction();action.doSomething();beans.xml使用mappedNamedo*批量匹配所有以do开头的方法beanidrealactionclasscom.chenhai.test.Action1/beanidbeforeAdviceclasscom.chenhai.test.MyBeforeAdvice/beanidadvisorclasscom.chenhai.aop.NameMatchMethodPointcutAdvisorpropertytypecom.chenhai.aop.AdvicenameadvicerefbeforeAdvice/propertytypeStringnamemappedNamevaluedo*//beanbeanidactionclasscom.chenhai.aop.ProxyFactoryBeanpropertytypejava.lang.Objectnametargetrefrealaction/propertytypeStringnameinterceptorNamevalueadvisor//bean因此doAction()和doSomething()都会先执行前置通知----------my interceptor before method call---------- real action doAction........ ----------my interceptor before method call---------- real action doSomething........整个判断过程可以先记成下面这条主线代理对象收到方法调用 ↓ 从 Advisor 取得 MethodInterceptor ↓ 从 Pointcut 取得 MethodMatcher ↓ 判断当前方法是否匹配 do* ↓ 命中执行增强再调用真实方法 未命中直接调用真实方法核心理解在当前 MiniSpring 教学版中AOP 代理的创建最终落在这一行代码上this.singletonInstancegetProxy(createAopProxy());createAopProxy()先根据配置组装目标对象和Advisor得到具体的JdkDynamicAopProxygetProxy()再调用 JDK 动态代理 API生成业务代码最终拿到的代理对象。哪个对象需要被代理、使用哪段增强逻辑、匹配哪些方法都写在 XML 中再由 IoC 容器通过 setter 自动注入。以上面的配置为例actionProxyFactoryBean ├─ target → realactionAction1被代理的真实对象 └─ interceptorName → advisor ↓ NameMatchMethodPointcutAdvisor ├─ advice → beforeAdviceMyBeforeAdvice具体增强逻辑 └─ mappedName → do*方法匹配规则所以ProxyFactoryBean先拿到targetrealaction和interceptorNameadvisor再从容器中取得NameMatchMethodPointcutAdvisor。这个Advisor已经由容器注入了beforeAdvice和do*前者决定“怎么增强”后者决定“哪些方法需要增强”。框架最后根据这些对象自动生成代理业务代码不需要手动创建或组装代理对象。二、先分清 Advice、Pointcut 和 Advisor切点可以理解成一个筛选条件有点像 SQL 中的where它只负责找出需要增强的方法不负责执行增强。本章几个核心对象的职责如下对象负责什么Advice/MethodInterceptor做什么增强Pointcut在哪里增强MethodMatcher判断当前方法是否命中PointcutAdvisor把增强逻辑和切点组合起来它们之间的关系是PointcutAdvisor ├─ MethodInterceptor怎么增强 └─ Pointcut └─ MethodMatcher当前方法是否匹配这样一来代理只负责组织调用不再关心具体匹配规则是方法名、注解还是其他条件。三、定义 Pointcut 和 MethodMatcher先定义切点接口。当前教学版只匹配方法所以Pointcut只需要提供MethodMatcherpackagecom.chenhai.aop;/** * 切点表示“在哪里增强”。 */publicinterfacePointcut{MethodMatchergetMethodMatcher();}MethodMatcher专门回答“这个方法要不要增强”packagecom.chenhai.aop;importjava.lang.reflect.Method;/** * 判断某个方法是否需要被 AOP 增强。 */publicinterfaceMethodMatcher{booleanmatches(Methodmethod,Class?targetClass);}这里同时传入了method和targetClass是为了让匹配器既能检查方法也能检查真实目标类。当前按名称匹配的实现只使用methodtargetClass暂时没有参与判断。完整的 Spring 切点还可以同时筛选目标类和方法。本章只保留方法匹配是为了先把最小调用链跑通。最后扩展Advisor让它同时提供增强和切点packagecom.chenhai.aop;/** * 带切点的 Advisor。 */publicinterfacePointcutAdvisorextendsAdvisor{PointcutgetPointcut();}普通Advisor只回答“怎么增强”PointcutAdvisor还可以回答“在哪里增强”。四、按方法名实现切点接下来实现NameMatchMethodPointcut。它既是Pointcut也直接承担MethodMatcher的匹配工作packagecom.chenhai.aop;importcom.chenhai.util.PatternMatchUtils;importjava.lang.reflect.Method;/** * 按方法名匹配的切点。 * * mappedName 可以是精确名称也可以包含简单通配符 *。 */publicclassNameMatchMethodPointcutimplementsMethodMatcher,Pointcut{privateStringmappedName;publicvoidsetMappedName(StringmappedName){// 避免后续匹配时出现空指针。this.mappedNamemappedNamenull?:mappedName;}publicStringgetMappedName(){returnthis.mappedName;}Overridepublicbooleanmatches(Methodmethod,Class?targetClass){if(methodnull){returnfalse;}StringmethodNamemethod.getName();returnthis.mappedName.equals(methodName)||isMatch(methodName,this.mappedName);}protectedbooleanisMatch(StringmethodName,StringmappedName){returnPatternMatchUtils.simpleMatch(mappedName,methodName);}OverridepublicMethodMatchergetMethodMatcher(){// 当前对象自己就能匹配方法所以直接返回 this。returnthis;}}支持的匹配方式包括mappedName可以匹配doAction精确匹配doActiondo*doAction、doSomething*Action所有以Action结尾的方法*Action*所有名称中包含Action的方法do*Action以do开头、以Action结尾的方法这里的*是简单通配符不是正则表达式也不支持?等其他通配符。真正的通配符算法放在工具类中切点只保留 AOP 相关职责publicstaticbooleansimpleMatch(Stringpattern,Stringstr){if(patternnull||strnull){returnfalse;}intfirstIndexpattern.indexOf(*);if(firstIndex-1){returnpattern.equals(str);}if(firstIndex0){if(pattern.length()1){returntrue;}intnextIndexpattern.indexOf(*,1);if(nextIndex-1){returnstr.endsWith(pattern.substring(1));}Stringpartpattern.substring(1,nextIndex);if(part.isEmpty()){returnsimpleMatch(pattern.substring(nextIndex),str);}intpartIndexstr.indexOf(part);while(partIndex!-1){StringremainingPatternpattern.substring(nextIndex);StringremainingStringstr.substring(partIndexpart.length());if(simpleMatch(remainingPattern,remainingString)){returntrue;}partIndexstr.indexOf(part,partIndex1);}returnfalse;}returnstr.length()firstIndexpattern.substring(0,firstIndex).equals(str.substring(0,firstIndex))simpleMatch(pattern.substring(firstIndex),str.substring(firstIndex));}这段算法的核心思路是*前面的内容必须精确匹配遇到*后再递归匹配剩余部分。匹配算法本身不是本章重点知道NameMatchMethodPointcut把它当成工具使用即可。五、用 PointcutAdvisor 组合切点和通知现在还差一个对象把Advice和NameMatchMethodPointcut放在一起packagecom.chenhai.aop;/** * 使用方法名切点的 Advisor。 */publicclassNameMatchMethodPointcutAdvisorimplementsPointcutAdvisor{privateAdviceadvice;privateMethodInterceptormethodInterceptor;privateStringmappedName;privatefinalNameMatchMethodPointcutpointcutnewNameMatchMethodPointcut();publicNameMatchMethodPointcutAdvisor(){}publicNameMatchMethodPointcutAdvisor(Adviceadvice){setAdvice(advice);}publicvoidsetAdvice(Adviceadvice){this.adviceadvice;// 统一适配成代理能够执行的 MethodInterceptor。setMethodInterceptor(adaptAdvice(advice));}publicAdvicegetAdvice(){returnthis.advice;}OverridepublicMethodInterceptorgetMethodInterceptor(){returnthis.methodInterceptor;}OverridepublicvoidsetMethodInterceptor(MethodInterceptormethodInterceptor){this.methodInterceptormethodInterceptor;}OverridepublicPointcutgetPointcut(){returnthis.pointcut;}publicvoidsetMappedName(StringmappedName){this.mappedNamemappedName;// XML 注入 mappedName 时同时更新内部切点。this.pointcut.setMappedName(mappedName);}publicStringgetMappedName(){returnthis.mappedName;}privateMethodInterceptoradaptAdvice(Adviceadvice){if(adviceinstanceofMethodBeforeAdvice){returnnewMethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);}if(adviceinstanceofAfterReturningAdvice){returnnewAfterReturningAdviceInterceptor((AfterReturningAdvice)advice);}if(adviceinstanceofMethodInterceptor){return(MethodInterceptor)advice;}thrownewIllegalArgumentException(Unsupported advice type: advice);}}XML 注入会触发两个关键 settersetAdvice()把前置、后置通知适配成统一的MethodInterceptorsetMappedName()把do*交给内部的NameMatchMethodPointcut。最终advisor同时持有了“怎么增强”和“增强哪些方法”。六、ProxyFactoryBean 要能识别 Advisor上一节的interceptorName只能指向一个普通Advice。现在 XML 中配置的是propertytypeStringnameinterceptorNamevalueadvisor/虽然属性名还叫interceptorName但它现在既可以指向Advice也可以指向完整的Advisor。ProxyFactoryBean需要先识别取出来的对象privatesynchronizedvoidinitializeAdvisor(){if(this.advisor!null||this.interceptorNamenull||this.interceptorName.trim().isEmpty()){return;}if(this.beanFactorynull){thrownewIllegalStateException(BeanFactory is required to resolve interceptorName);}ObjectinterceptorOrAdvisor;try{interceptorOrAdvisorthis.beanFactory.getBean(this.interceptorName);}catch(BeansExceptione){thrownewIllegalStateException(Resolve interceptor failed: this.interceptorName,e);}this.advisoradaptAdvisor(interceptorOrAdvisor);}privateAdvisoradaptAdvisor(ObjectinterceptorOrAdvisor){if(interceptorOrAdvisorinstanceofAdvisor){// Advisor 已经包含通知PointcutAdvisor 还会包含切点。return(Advisor)interceptorOrAdvisor;}if(!(interceptorOrAdvisorinstanceofAdvice)){thrownewIllegalArgumentException(Unsupported interceptor/advisor type: interceptorOrAdvisor);}// 兼容上一章单独的 Advice 包成不带切点的 DefaultAdvisor。DefaultAdvisordefaultAdvisornewDefaultAdvisor();defaultAdvisor.setMethodInterceptor(adaptAdvice((Advice)interceptorOrAdvisor));returndefaultAdvisor;}这里保留了对上一章配置方式的兼容配置PointcutAdvisor按切点选择方法配置普通Advice包装成DefaultAdvisor对所有方法执行增强。七、动态代理根据切点决定是否增强最后回到JdkDynamicAopProxy.invoke()。原来写死的doAction被替换成了shouldApply(method)OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{MethodInterceptorinterceptorgetMethodInterceptor();if(interceptor!nullshouldApply(method)){Class?targetClassthis.targetnull?null:this.target.getClass();MethodInvocationinvocationnewReflectiveMethodInvocation(proxy,this.target,method,args,targetClass);returninterceptor.invoke(invocation);}// 没有命中切点的方法仍然正常调用真实对象。returninvokeTarget(method,args);}privatebooleanshouldApply(Methodmethod){if(this.advisornull){returnfalse;}if(!(this.advisorinstanceofPointcutAdvisor)){// 不带切点的普通 Advisor 默认匹配所有方法。returntrue;}Class?targetClassthis.targetnull?null:this.target.getClass();Pointcutpointcut((PointcutAdvisor)this.advisor).getPointcut();returnpointcut!nullpointcut.getMethodMatcher()!nullpointcut.getMethodMatcher().matches(method,targetClass);}判断逻辑分为三种情况没有Advisor不执行增强是普通Advisor默认对所有方法执行增强是PointcutAdvisor只有MethodMatcher匹配成功才执行增强。无论有没有命中切点真实方法都要执行。切点只控制增强是否介入不能让未命中的方法直接返回null。八、验证匹配结果项目中的测试直接验证了do*的匹配结果NameMatchMethodPointcutpointcutnewNameMatchMethodPointcut();pointcut.setMappedName(do*);assertTrue(pointcut.matches(IAction.class.getMethod(doAction),Action1.class));assertTrue(pointcut.matches(IAction.class.getMethod(doSomething),Action1.class));assertFalse(pointcut.matches(Object.class.getMethod(toString),Action1.class));它还会通过容器取得代理对象验证两个do方法都先执行前置通知再执行真实方法。这样既检查了匹配器也检查了 XML、Advisor和动态代理组成的完整调用链。当前版本仍然是一个教学实现一个NameMatchMethodPointcut只保存一个mappedName只按方法名匹配还不支持类、注解和参数类型等条件一个代理仍然只配置一个Advisor还没有多拦截器链。小结这一节把硬编码的方法名判断抽成了独立切点Advice做什么增强 Pointcut在哪里增强 Advisor把二者组合起来 JdkDynamicAopProxy匹配成功后执行增强以后再增加匹配规则时只需要实现新的Pointcut或MethodMatcher不必继续修改具体的动态代理逻辑。AOP的核心要点都围绕this.singletonInstance getProxy(createAopProxy())这行代码展开最终就是为了让框架自动生成代理对象。然后注意哪个类需要被代理具体怎么代理增强用哪个代理增强类都写在配置文件里通过IOC容器自动set注入依赖关系。先拿到需要被代理的对象targetinterceptorName–》com.chenhai.aop.NameMatchMethodPointcutAdvisor然后通过set注入Advice具体增强类、mappedName匹配规则

相关新闻

LangChain与Elasticsearch构建智能知识库实战

LangChain与Elasticsearch构建智能知识库实战

1. 项目概述在AI技术快速发展的今天,构建能够理解和处理专业知识的智能代理(agent)已成为许多企业和开发者的迫切需求。LangChain作为当前最热门的大语言模型应用框架,与Elasticsearch这一强大的搜索引擎相结合,可以打造出真正"懂行&quo…

2026/7/29 1:37:55 阅读更多 →
AI不确定推理实战:5种Python算法提升系统鲁棒性

AI不确定推理实战:5种Python算法提升系统鲁棒性

在人工智能的早期发展中,系统往往依赖于非黑即白的确定推理。然而,现实世界充满了噪声、缺失数据和模糊信息。不确定推理作为人工智能世界里最诚实的一面,承认了人类认知和客观环境的局限性,通过概率和模糊数学来量化未知。本文将…

2026/7/29 1:37:55 阅读更多 →
终极AriaNg GUI下载工具指南:如何实现多线程高速下载

终极AriaNg GUI下载工具指南:如何实现多线程高速下载

终极AriaNg GUI下载工具指南:如何实现多线程高速下载 【免费下载链接】aria-ng-gui 一个 Aria2 图形界面客户端 | An Aria2 GUI for Windows & Linux & MacOS 项目地址: https://gitcode.com/gh_mirrors/ar/aria-ng-gui AriaNg GUI是一款专业的Aria2…

2026/7/29 1:37:55 阅读更多 →

最新新闻

FPG财盛国际:把执行效率做扎实,更谨慎的使用者更容易感受到的维度

FPG财盛国际:把执行效率做扎实,更谨慎的使用者更容易感受到的维度

外汇市场信息更新频繁,平台口碑的形成更依赖长期一致性:入口是否好找、说明是否前后一致、提示是否稳定出现。围绕FPG财盛国际,下面从稳定体验与信息呈现等角度做一次正面观察。外汇相关信息更新频繁,平台将关键提示与解释呈现得更…

2026/7/29 1:50:00 阅读更多 →
168、NPU的编译器开发:异常处理与错误恢复

168、NPU的编译器开发:异常处理与错误恢复

嵌入式NPU原理基础:从零开始理解神经网络处理器 第168章 NPU的编译器开发:异常处理与错误恢复 从一次凌晨三点的崩溃说起 凌晨三点,我盯着屏幕上那行冰冷的错误码发呆: NPU_ERR: IRQ_HANDLER_TIMEOUT at tile[2], core[3], cycle[4523891]这是某款车规级NPU芯片的现场。…

2026/7/29 1:50:00 阅读更多 →
比较好的写字楼装修公司怎么选?广州森宇设计靠谱工装公司推荐

比较好的写字楼装修公司怎么选?广州森宇设计靠谱工装公司推荐

企业准备装修写字楼时,通常会同时接触几家公司。有的案例多,有的报价低,还有的承诺设计、施工、消防和空调都能一并完成。表面看差别不大,真正开工后却可能出现方案与现场脱节、报价不断增加、不同专业互相推责等问题。因此&#…

2026/7/29 1:50:00 阅读更多 →
167、NPU的编译器开发:条件执行与分支处理

167、NPU的编译器开发:条件执行与分支处理

NPU的编译器开发:条件执行与分支处理 上周五晚上十一点,我在调试一个客户反馈的模型推理异常——一个简单的if-else分支,在ARM CPU上跑得好好的,迁移到NPU后输出全乱。盯着反汇编出来的NPU指令流看了两个小时,发现编译器把条件分支优化成了两条并行路径,但两条路径的中间…

2026/7/29 1:50:00 阅读更多 →
消费级外骨骼三条技术路线:适老、户外与轻量化如何选择?

消费级外骨骼三条技术路线:适老、户外与轻量化如何选择?

消费级外骨骼的竞争重点,已经从“能不能提供助力”转向“为谁助力、在哪助力、如何补能”。海尔W1、W2、W3分别代表柔性适老、全地形运动和轻量全场景三条路线。这三条路线没有共同的最优参数。产品是否匹配,取决于步态特征、使用时长和移动场景。## 路线…

2026/7/29 1:50:00 阅读更多 →
Arduino与ML8511紫外线传感器:从数据采集到环境监测的DIY实践

Arduino与ML8511紫外线传感器:从数据采集到环境监测的DIY实践

1. 项目缘起:为什么要在海盗船上加装紫外线检测?如果你和我一样,是个喜欢捣鼓各种电子小玩意儿的创客,那么“海盗船”这个项目对你来说可能并不陌生。它可能是一个桌面摆件,一个遥控玩具,或者像我这样&…

2026/7/29 1:49:00 阅读更多 →

日新闻

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:00:23 阅读更多 →
AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础 在上一期「AI编程系列」中,我们学习了如何构建一个基础的 AI 问答系统,通过简单的输入输出让模型回应问题。但现实世界中的 AI 应用往往需要处理更复杂的场景:…

2026/7/29 0:00:23 阅读更多 →
AI智能体开发实战:从工具调用到企业级部署

AI智能体开发实战:从工具调用到企业级部署

1. 从被动问答到主动执行:AI Agent的范式转变过去两年,大语言模型最显著的应用形态是聊天机器人——用户提问,AI回答。但真正的生产力革命发生在2023年下半年:当AI学会主动调用工具完成任务时,生产力工具的历史被彻底改…

2026/7/29 0:00:23 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻