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/10/17 8:53:18 阅读更多 →
AI不确定推理实战:5种Python算法提升系统鲁棒性

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

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

2026/10/17 8:53:25 阅读更多 →
终极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/10/17 8:53:29 阅读更多 →

最新新闻

结构化Diagram设计:让流程图变成可执行、可验证的系统逻辑

结构化Diagram设计:让流程图变成可执行、可验证的系统逻辑

1. 项目概述:这不是画图,是构建可执行的系统逻辑骨架“diagram-design”这个标题乍看像一个设计类小工具,但在我过去十年带过的几十个跨领域项目里,它从来不是PPT里的示意图,而是工程师、产品经理、硬件调试员和算法研…

2026/10/19 6:13:43 阅读更多 →
Cat.1模组内置GNSS实操:从AT指令到NMEA解析与低功耗设计

Cat.1模组内置GNSS实操:从AT指令到NMEA解析与低功耗设计

做带定位功能的产品,之前最标准的做法基本就是“MCU GPS芯片 4G模组”三件套拼在一起,软件上还要同时处理两路串口、两套电源时序和定位数据转发。这几年中低速物联网市场里,很多 Cat.1 模组直接把 GNSS 集成进去了,比如我手上的…

2026/10/19 6:13:43 阅读更多 →
decimal精度陷阱:金额计算为何不能用float

decimal精度陷阱:金额计算为何不能用float

1. 从一个“算错钱”的线上事故说起很多刚接触数据库开发的朋友,第一次被decimal坑到,往往不是在什么高深的技术场景里,而是在最朴素的地方——算钱。我印象特别深,早些年参与过一个内部记账模块的维护,测试环境跑得好…

2026/10/19 6:13:43 阅读更多 →
2025无偏渲染引擎全景对比:Arnold、V-Ray、Redshift等六款渲染器选型指南

2025无偏渲染引擎全景对比:Arnold、V-Ray、Redshift等六款渲染器选型指南

做CG这一行的,应该都躲不开渲染器选型这件事。打开社区论坛,2025年关于无偏渲染引擎的讨论热度比前几年高了不止一个量级,身边做建筑可视化的朋友已经把主力换成了Corona或者V-Ray的GPU模式,影视团队还在用Arnold稳扎稳打出片&…

2026/10/19 6:13:43 阅读更多 →
diagram-design:从草图到系统,如何设计出能评审、能落地的技术图表

diagram-design:从草图到系统,如何设计出能评审、能落地的技术图表

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题第一次看到 “diagram-design” 这个标题,很多人会以为它只是教你怎么把图画得好看一点。但真正做过技术方案、写过设计文档、给团队讲过架构的人都知道,画图这件事从来不只是“美…

2026/10/19 6:13:43 阅读更多 →
artcraft:可调试、可复现的数字艺术工艺实践

artcraft:可调试、可复现的数字艺术工艺实践

1. 项目概述:从“artcraft”这个词开始,我们到底在做什么?“artcraft”这个词乍看像一个合成词,拆开是 art(艺术)和 craft(工艺、手艺),合起来直译就是“艺术工艺”&…

2026/10/19 6:12:42 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →