自己动手写一个spring之aop_2
写在前面在这篇文章 中我们已经使得AOP有了雏形但是代理逻辑的执行还是硬编码的如下OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{if(method.getName().equals(doAction)){System.out.println(-----通过jdk动态代理代理了类的doAction方法........);returnmethod.invoke(target,args);}returnnull;}当然这样做呢不能算错但是扩展性就要弱很多了在不同的业务场景中要做的内容不同该怎么办呢本文就一起来把这个问题解决了。1正文对于具体业务方法执行过程的干预我们可以抽象出一个增强的概念即增强业务方法本身的能力如开始没有性能统计的能力通过AOP就能增加这部分能力为此定义一个类似于标记的接口如下packagecom.hc.minispring.web.v7_aop.aop;/** * 切面概念接口更加具象化使用方法拦截器进行增强织入 */publicinterfaceAdvisor{MethodInterceptorgetMethodInterceptor();voidsetMethodInterceptor(MethodInterceptormethodInterceptor);}再来定义一个更加具体的拦截接口代表要通过拦截的方式来增强packagecom.hc.minispring.web.v7_aop.aop;/** * 基于拦截的抽象这里拦截也是一个抽象的概念并不限定于如何拦截 */publicinterfaceInterceptorextendsAdvice{}在定义一个更加具体的基于方法执行拦截的接口/** * 具体的基于方法的拦截也是我们实现AOP方法动态增强织入的关键 */publicinterfaceMethodInterceptorextendsInterceptor{/** * 增强逻辑执行invocation负责执行目标方法 * param invocation * return * throws Throwable */Objectinvoke(MethodInvocationinvocation)throwsThrowable;}当然MethodInterceptor的具体实现类要由用户来指定这里给出一个追踪方法执行过程的实现类以供测试使用当然仅仅是个例子了packagecom.hc.minispring.web.v7_aop.aop;/** * 方法调用追踪 */publicclassTracingInterceptorimplementsMethodInterceptor{publicObjectinvoke(MethodInvocationi)throwsThrowable{System.out.println(即将执行方法 i.getMethod());Objectreti.proceed();System.out.println(执行方法 i.getMethod() 成功*★,°*:.☆(▽)/$:*.°★* 。);returnret;}}为了能够更加明确的表达要做的事情再来定义一个类似于Advice不真正干活的接口Advisor切面抽象出的概念因为对方法执行的拦截就好像拦了一个面一样如下packagecom.hc.minispring.web.v7_aop.aop;/** * 切面概念接口更加具象化使用方法拦截器进行增强织入 */publicinterfaceAdvisor{MethodInterceptorgetMethodInterceptor();voidsetMethodInterceptor(MethodInterceptormethodInterceptor);}可以看出真正干活的还是MethodInterceptor给出实现类/** * 默认实现简单仅仅是维护了方法拦截器信息更多的是Advisor对于功能的表达语义 * 即更加形象化的存在并不做具体的工作 */publicclassDefaultAdvisorimplementsAdvisor{privateMethodInterceptormethodInterceptor;publicDefaultAdvisor(){}publicvoidsetMethodInterceptor(MethodInterceptormethodInterceptor){this.methodInterceptormethodInterceptor;}publicMethodInterceptorgetMethodInterceptor(){returnthis.methodInterceptor;}}接着就来改造现有的类了从生成代理类的FacotryBean入手这里是com.hc.minispring.aop.ProxyFactoryBean,修改如下packagecom.hc.minispring.aop;// .../** * 真正返回代理bean的工厂bean */publicclassProxyFactoryBeanimplementsFactoryBeanObject{// .../** 切面相关内容 **/privateStringinterceptorName;privateAdvisoradvisor;/** 切面相关内容 **/// ...protectedAopProxycreateAopProxy(){// System.out.println(----------createAopProxy for : target --------);// return getAopProxyFactory().createAopProxy(target);// 修改点创建代理类时增加advisor参数用来执行织入的增强逻辑returngetAopProxyFactory().createAopProxy(target,advisor);}// ...OverridepublicObjectgetObject()throwsException{// 修改点初始化切面initializeAdvisor();returngetSingletonInstance();}// 修改点privatesynchronizedvoidinitializeAdvisor(){Objectadvicenull;MethodInterceptorminull;try{// advice (MethodInterceptor) this.beanFactory.getBean(this.interceptorName);// 正常应该从容器中获取但是这里为了走通流程忽略这个次要矛盾了advicenewTracingInterceptor();// } catch (BeansException e) {}catch(Exceptione){e.printStackTrace();}advisornewDefaultAdvisor();// 给切面设置用户给定的方法拦截器即用户自定义的增强逻辑advisor.setMethodInterceptor((MethodInterceptor)advice);}// ...}advisor参数一路传最终传给负责真正干活的代理类JdkDynamicAopProxy,同时修改其逻辑使用methodinterceprot执行增强的织入逻辑packagecom.hc.minispring.aop;// ...publicclassJdkDynamicAopProxyimplementsAopProxy,InvocationHandler{Objecttarget;// 修改点增加切面Advisoradvisor;publicJdkDynamicAopProxy(Objecttarget,Advisoradvisor){this.targettarget;this.advisoradvisor;}OverridepublicObjectgetProxy(){ObjectobjProxy.newProxyInstance(JdkDynamicAopProxy.class.getClassLoader(),target.getClass().getInterfaces(),this);System.out.println(----------创建基于jdk的动态代理类成功 ---------obj);returnobj;}OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{if(method.getName().equals(doAction)){Class?targetClass(target!null?target.getClass():null);// 修改点使用切面执行逻辑MethodInterceptorinterceptorthis.advisor.getMethodInterceptor();MethodInvocationinvocationnewReflectiveMethodInvocation(proxy,target,method,args,targetClass);returninterceptor.invoke(invocation);}returnnull;}}先来测试一下conrollerRequestMapping(/hello1746)// public String doTest(String name) {// public String doTest(/*HttpServletRequest request, HttpServletResponse response*/) {publicStringdoTest(/*HttpServletRequest request, HttpServletResponse response*/){System.out.println(action: action.getClass());action.doAction();returnindex;}}运行截至这里源码 。我们继续来优化。在很多时候我们可能只需要在方法执行前进行增强或者是在方法执行后进行增强。当然目前的方式也能够实现但是对于用户来说还是不够简洁并且也不够明确所以我们来进一步进行优化即分别定义执行前后的增强方法执行前后的增强方法执行前后的拦截如下4个接口2个类执行前的增强方法执行前的增强方法执行前的拦截/** * 前置增强 */publicinterfaceBeforeAdviceextendsAdvice{}/** * 方法前置增强 */publicinterfaceMethodBeforeAdviceextendsBeforeAdvice{voidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable;}/** * 方法前置拦截器包装方法前置增强 */publicclassMethodBeforeAdviceInterceptorimplementsMethodInterceptor{privatefinalMethodBeforeAdviceadvice;publicMethodBeforeAdviceInterceptor(MethodBeforeAdviceadvice){this.adviceadvice;}OverridepublicObjectinvoke(MethodInvocationmi)throwsThrowable{// 方法执行前插入的逻辑由用户给出具体的实现this.advice.before(mi.getMethod(),mi.getArguments(),mi.getThis());// 目标方法执行returnmi.proceed();}}执行后的增强方法执行后的增强方法执行后的拦截/** * 后置增强 */publicinterfaceAfterAdviceextendsAdvice{}/** * 方法后置增强 */publicinterfaceAfterReturningAdviceextendsAfterAdvice{voidafterReturning(ObjectreturnValue,Methodmethod,Object[]args,Objecttarget)throwsThrowable;}publicclassAfterReturningAdviceInterceptorimplementsMethodInterceptor,AfterAdvice{privatefinalAfterReturningAdviceadvice;publicAfterReturningAdviceInterceptor(AfterReturningAdviceadvice){this.adviceadvice;}OverridepublicObjectinvoke(MethodInvocationmi)throwsThrowable{ObjectretValmi.proceed();this.advice.afterReturning(retVal,mi.getMethod(),mi.getArguments(),mi.getThis());returnretVal;}}修改proxyfactorybean的initAdvisor方法privatesynchronizedvoidinitializeAdvisor(){Objectadvicenull;MethodInterceptorminull;try{// advice (MethodInterceptor) this.beanFactory.getBean(this.interceptorName);// 正常应该从容器中获取但是这里为了走通流程忽略这个次要矛盾了// advice new TracingInterceptor();try{// advice new TracingInterceptor();advicenewMethodBeforeAdvice(){Overridepublicvoidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println(method: method.getName() before execute***********!!!);}};}catch(Exceptione){e.printStackTrace();}if(adviceinstanceofBeforeAdvice){minewMethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);}elseif(adviceinstanceofAfterAdvice){minewAfterReturningAdviceInterceptor((AfterReturningAdvice)advice);}elseif(adviceinstanceofMethodInterceptor){mi(MethodInterceptor)advice;}advisornewDefaultAdvisor();advisor.setMethodInterceptor(mi);// } catch (BeansException e) {}catch(Exceptione){e.printStackTrace();}// advisor new DefaultAdvisor();// // 给切面设置用户给定的方法拦截器即用户自定义的增强逻辑// advisor.setMethodInterceptor((MethodInterceptor) advice);}根据不同的类型增强进行不同的处理这里作为测试写死了一个方法前置增强运行测试截至这里源码 。spring中还有环绕通知但基本上一个道理。写在后面参考文章列表

相关新闻

5步打造你的GTA5终极防护盾:YimMenu完整使用指南

5步打造你的GTA5终极防护盾:YimMenu完整使用指南

5步打造你的GTA5终极防护盾:YimMenu完整使用指南 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu …

2026/10/16 19:26:16 阅读更多 →
【单片机课程设计/毕业设计】基于 STM32 的洗衣多流程时序控制方案研究 基于单片机继电器驱动洗衣模拟装置开发(015701)

【单片机课程设计/毕业设计】基于 STM32 的洗衣多流程时序控制方案研究 基于单片机继电器驱动洗衣模拟装置开发(015701)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/10/17 13:17:26 阅读更多 →
【单片机课程设计/毕业设计】基于嵌入式技术的室内安防多传感一体化监测系统 基于 STM32 的燃气检测温控风扇人体照明控制系统(015601)

【单片机课程设计/毕业设计】基于嵌入式技术的室内安防多传感一体化监测系统 基于 STM32 的燃气检测温控风扇人体照明控制系统(015601)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/10/17 13:17:26 阅读更多 →

最新新闻

3ds Max森林插件Forest Pack 7散布引擎原理与性能优化实战

3ds Max森林插件Forest Pack 7散布引擎原理与性能优化实战

简介:这份PDF是3ds Max森林树木植物散布插件Forest Pack 7的官方帮助文档,面向建筑可视化、游戏开发与影视特效领域的三维设计师,尤其适合需要搭建大规模自然或城市景观的中高级用户。Forest Pack作为最流行的3ds Max植物散布插件&#xff0c…

2026/10/17 22:05:06 阅读更多 →
为什么关键词工具总是慢两周?new-term-radar:AI新词雷达完全指南

为什么关键词工具总是慢两周?new-term-radar:AI新词雷达完全指南

【免费下载链接】new-term-radar Daily radar for new AI terms: names of new models, products and repos, caught on X, Hugging Face, GitHub and sitemaps before keyword tools list them. 项目地址: https://gitcode.com/gh_mirrors/ne/new-term-radar 点击查…

2026/10/17 22:05:06 阅读更多 →
移动端调试工具实战:vConsole、Eruda与Spy-Debugger选型指南

移动端调试工具实战:vConsole、Eruda与Spy-Debugger选型指南

做H5/WAP开发的人应该都有过这种体验:桌面上把页面调得妥妥当当,一扔到手机里就“原形毕露”,报错信息看不到、接口状态不知道、元素位置对不上。PC端一个F12能解决的事,到移动端要同时面对浏览器兼容、真机差异、WebView内核差异…

2026/10/17 22:05:06 阅读更多 →
Ajax请求中hide与show时序控制:浏览器渲染机制与四种解决方案

Ajax请求中hide与show时序控制:浏览器渲染机制与四种解决方案

1. 从一个“按钮点下去没反应”的经典场景说起做前端开发的人,几乎都遇到过这样一种情况:用户点了一个按钮,你希望先把某个区域隐藏起来,等数据从服务端回来之后再显示新的内容。逻辑上看起来天衣无缝——先hide(),再发…

2026/10/17 22:05:06 阅读更多 →
FrankenPHP 原生支持 Windows:PHP 应用服务器新选择

FrankenPHP 原生支持 Windows:PHP 应用服务器新选择

FrankenPHP 原生支持 Windows 了。这句话如果放在两年前,可能只是又一个热门项目的更新日志,但真在 Windows 上折腾过 FrankenPHP 的开发者,应该知道其中的分量。这是一个基于 Caddy 的 PHP 应用服务器,内置 PHP-FPM,可…

2026/10/17 22:05:06 阅读更多 →
Flutter for OpenHarmony 实战:将番茄钟与习惯打卡融合的效率助手

Flutter for OpenHarmony 实战:将番茄钟与习惯打卡融合的效率助手

我一直觉得,效率工具最大的问题不是功能不够多,而是功能之间彼此孤立。你打开一个番茄钟应用,专注完了关掉,再打开一个习惯打卡应用,记录一笔,数据零零散散,最后根本形成不了正向反馈。这次用 F…

2026/10/17 22:04:04 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 14:23:36 阅读更多 →