当前位置: 首页 > news >正文

自己动手写一个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());Objectret=i.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.methodInterceptor=methodInterceptor;}publicMethodInterceptorgetMethodInterceptor(){returnthis.methodInterceptor;}}

接着就来改造现有的类了,从生成代理类的FacotryBean入手,这里是com.hc.minispring.aop.ProxyFactoryBean,修改如下:

packagecom.hc.minispring.aop;// .../** * 真正返回代理bean的工厂bean */publicclassProxyFactoryBeanimplementsFactoryBean<Object>{// .../** 切面相关内容 **/privateStringinterceptorName;privateAdvisoradvisor;/** 切面相关内容 **/// ...protectedAopProxycreateAopProxy(){// System.out.println("----------createAopProxy for :" + target + "--------");// return getAopProxyFactory().createAopProxy(target);// 修改点:创建代理类时增加advisor参数,用来执行织入的增强逻辑returngetAopProxyFactory().createAopProxy(target,advisor);}// ...@OverridepublicObjectgetObject()throwsException{// 修改点:初始化切面initializeAdvisor();returngetSingletonInstance();}// 修改点:privatesynchronizedvoidinitializeAdvisor(){Objectadvice=null;MethodInterceptormi=null;try{// advice = (MethodInterceptor) this.beanFactory.getBean(this.interceptorName);// 正常应该从容器中获取,但是这里为了走通流程,忽略这个次要矛盾了!!!advice=newTracingInterceptor();// } catch (BeansException e) {}catch(Exceptione){e.printStackTrace();}advisor=newDefaultAdvisor();// 给切面设置用户给定的方法拦截器,即用户自定义的增强逻辑advisor.setMethodInterceptor((MethodInterceptor)advice);}// ...}

advisor参数一路传,最终传给负责真正干活的代理类JdkDynamicAopProxy,同时修改其逻辑使用methodinterceprot执行增强的织入逻辑:

packagecom.hc.minispring.aop;// ...publicclassJdkDynamicAopProxyimplementsAopProxy,InvocationHandler{Objecttarget;// 修改点:增加切面Advisoradvisor;publicJdkDynamicAopProxy(Objecttarget,Advisoradvisor){this.target=target;this.advisor=advisor;}@OverridepublicObjectgetProxy(){Objectobj=Proxy.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);// 修改点:使用切面执行逻辑MethodInterceptorinterceptor=this.advisor.getMethodInterceptor();MethodInvocationinvocation=newReflectiveMethodInvocation(proxy,target,method,args,targetClass);returninterceptor.invoke(invocation);}returnnull;}}

先来测试一下,conroller:

@RequestMapping("/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();return"index";}}

运行:

截至这里源码 。
我们继续来优化。
在很多时候我们可能只需要在方法执行前进行增强,或者是在方法执行后进行增强。当然目前的方式也能够实现,但是对于用户来说还是不够简洁,并且也不够明确,所以我们来进一步进行优化,即分别定义执行前后的增强方法执行前后的增强方法执行前后的拦截,如下4个接口,2个类:

  • 执行前的增强,方法执行前的增强,方法执行前的拦截
/** * 前置增强 */publicinterfaceBeforeAdviceextendsAdvice{}/** * 方法前置增强 */publicinterfaceMethodBeforeAdviceextendsBeforeAdvice{voidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable;}/** * 方法前置拦截器(包装方法前置增强) */publicclassMethodBeforeAdviceInterceptorimplementsMethodInterceptor{privatefinalMethodBeforeAdviceadvice;publicMethodBeforeAdviceInterceptor(MethodBeforeAdviceadvice){this.advice=advice;}@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.advice=advice;}@OverridepublicObjectinvoke(MethodInvocationmi)throwsThrowable{ObjectretVal=mi.proceed();this.advice.afterReturning(retVal,mi.getMethod(),mi.getArguments(),mi.getThis());returnretVal;}}

修改proxyfactorybean的initAdvisor方法:

privatesynchronizedvoidinitializeAdvisor(){Objectadvice=null;MethodInterceptormi=null;try{// advice = (MethodInterceptor) this.beanFactory.getBean(this.interceptorName);// 正常应该从容器中获取,但是这里为了走通流程,忽略这个次要矛盾了!!!// advice = new TracingInterceptor();try{// advice = new TracingInterceptor();advice=newMethodBeforeAdvice(){@Overridepublicvoidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println("method: "+method.getName()+" before execute***********!!!");}};}catch(Exceptione){e.printStackTrace();}if(adviceinstanceofBeforeAdvice){mi=newMethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);}elseif(adviceinstanceofAfterAdvice){mi=newAfterReturningAdviceInterceptor((AfterReturningAdvice)advice);}elseif(adviceinstanceofMethodInterceptor){mi=(MethodInterceptor)advice;}advisor=newDefaultAdvisor();advisor.setMethodInterceptor(mi);// } catch (BeansException e) {}catch(Exceptione){e.printStackTrace();}// advisor = new DefaultAdvisor();// // 给切面设置用户给定的方法拦截器,即用户自定义的增强逻辑// advisor.setMethodInterceptor((MethodInterceptor) advice);}

根据不同的类型增强,进行不同的处理,这里作为测试写死了一个方法前置增强,运行测试:

截至这里源码 。

spring中还有环绕通知,但基本上一个道理。

写在后面

参考文章列表

http://www.jsqmd.com/news/1302060/

相关文章:

  • FPGA中UART串口模块
  • 目前口碑出众的机器人视觉识别系统都有哪几款值得我们参考选择 - 甄选测评馆
  • 转大模型后,我为什么把权限和日志写得比 Prompt 还仔细
  • Anthropic 承认 Claude 在隔离测试中黑入三家组织:和 OpenAI 接连翻车,AI 安全测试的威胁模型正在失效
  • 阜阳专科毕业升本科,如何比较机构的服务完整度 - 精彩城市
  • 重庆钻石回收迈入“全域直通时代”!行情一站透明无差价,7家合规门店开通钻石鉴定回收专线 - 二奢分享官
  • 闲置克罗心首饰别放着贬值!专业渠道盘活潮流珠宝,告别估价悬殊烦恼 - 全国二奢机构参考
  • 免费获取百度文库文档:终极指南与完整教程
  • Stability AI生成式模型:从2D到4D的视觉革命
  • 技术债务的偿还策略:如何说服业务方投资数据库架构升级
  • 2026年国内酒店用品厂家排行 适配不同酒店采购需求 - 甄选测评馆
  • 技术探秘:Windows 10上实现Android应用生态桥接的完整指南
  • 美丽蓝天大气治理补贴申报技术要点与落地解决方案 —— 越华环保一体化申报服务体系
  • 2026年武汉地区上上电缆代理厂家排行 适配多场景采购需求 - 甄选测评馆
  • 从MOD到MP3:OpenMPT音频导出全流程教程
  • 1688找工厂名片AI大改版:从7星到10星,商家该如何应对
  • 2026年绵阳装修公司挑选攻略:铭典装饰等优质企业实测梳理 - 比奇堡111
  • 2026深圳名包回收优选名录:7家正规机构实测对 - 全国二奢机构参考
  • 长沙买猫避坑指南!实测多家黑店,认准这家正规连锁猫舍 - 资讯报道
  • 本地大模型部署与API调用实战:LM Studio指南
  • 太仓淋浴房哪家性价比高?之江国际实体门店|欧卫特淋浴房靠谱优选 - 甄选测评馆
  • 零延迟流媒体革命:go2rtc终极摄像头接入解决方案 [特殊字符]
  • 30KG重载机器人分析:柔性力控与恒力打磨,越疆智能架构构筑优势
  • 5分钟快速上手:Kafka-UI可视化监控平台的完整使用指南
  • 【单片机课程设计/毕业设计】基于嵌入式技术的室内安防多传感一体化监测系统 基于 STM32 的燃气检测温控风扇人体照明控制系统(015601)
  • Illustrator脚本大全:从入门到精通的自动化设计指南
  • UnityShader Linear01Depth和LinearEyeDepth函数
  • 5分钟学会CartoonGAN-Test-Pytorch-Torch:简单命令行实现图片卡通化
  • 跨境电商指纹浏览器选型:AdsPower替代方案如何比较功能、成本与迁移条件
  • 众岸公考客观测评|打破地域误区:不止适配山东,全国考生均可选择 - 互联网科技品牌测评