`

Spring AOP介绍及源码分析

    博客分类:
  • Java
阅读更多

Spring AOP介绍及源码分析

copy from http://www.goldendoc.org/2010/12/spring_aop/ 

 

软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程;前者是为了提高开发效率,而后者则使用了归纳法,把具有共性的东西进行归类并使之模块化,达到便于维护和扩展的目的;如果说面向对象编程可以对业务需求进行很好的分解使之模块化;那么面向切面编程AOP(Aspect-Oriented Programming)则可以对系统需求进行很好的模软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程;前者是为了提高开发效率,而后者则使用了归纳法,把具有共性的东西进行归类并使之模块化,达到便于维护和扩展的目的;如果说面向对象编程可以对业务需求进行很好的分解使之模块化;那么面向切面编程AOP(Aspect-Oriented Programming)则可以对系统需求进行很好的模块组织,简化系统需求和实现之间的对比关系,是对OOP思想的一种补充;块组织,简化系统需求和实现之间的对比关系,是对OOP思想的一种补充;

 

一、AOP介绍

举个例子来说明一下吧!现在系统中有很多的业务方法,如上传产品信息、修改产品信息、发布公司库等;现在需要对这些方法的执行做性能监控,看每个业务方法的执行时间;在不改变原业务代码的基础上,也许我们会这么做:

Offer接口:


Offer实现:


Offer代理:


我们要通过下面的方式来使用:


上面的例子的输出为:


上面的例子中,OfferProxy实现了IOffer,而所有的业务实现均委托给其成员offer;可以想像,这应该就是最简单的AOP的实现了;但这种方式会存在一个问题:如果有非常多的这种业务对象需要性能监控,我们就需要写同样多的XyzProxy来满足需求,这也是非常巨大的工作量。

上面说到了代理,我们先看看代理模式吧!

 

二、代理模式及实现

下面是代理模式的类图:


代理模式类图

代理模式中,存在一个称为ProxyObject的代理对象和RealObject的真实对象,它们都实现了相同的接口;在调用的地方持有ProxyObject的实例,当调用request()方法时,ProxyObject可以在执行RealObject.request()前后做一些特定的业务,甚至不调用RealObject.request()方法。

目前实现代理模式的方式有两种:基于JDK的动态代理和基于CGLIB字节码的代理。

2.1 JDK动态代理

JDK动态代理,顾名思义,是基于JDK的反射(reflect)机制;在JDK中,提供了InvocationHandler这个接口,下面是JDK里面的注释:

 

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

 

简单翻译,意思是说:该接口由被代理对象的handler所实现;当调用代理对象的方法时,该方法调用将被编码,然后交给代理对象的invoke方法去执行;

是不是有一种豁然开朗的感觉呢?没错,答案就在你心中。

这样,上面的代码就可以改成下面的实现方式:


调用端:


通过这种方式,你不需要为针对每一个业务写一个代理对象,就可以很轻松地完成你的需求;但也许你已经注意到了,JDK的动态代理,在创建代理对象(上面红色代码部分)时,被代理的对象需要实现接口(即面向接口编程);

这就是JDK的动态代理,简单吧!下面看看CGLIB代理方式。

2.2 CGLIB代理

如果目标对象没有实现任何接口,那怎么办呢?不用担心,你可以用CGLIB来实现代理:


调用端:


使用CGLIB创建的代理对象,其实就是继承了要代理的目标类,然后对目标类中所有非final方法进行覆盖,但在覆盖方法时会添加一些拦截代码(上面CglibProxyFactory类中的intercept方法)。

下面看看Spring中是如何实现AOP的。

 

三、Spring AOP的实现

 

3.1 Spring AOP的几个概念

Spring AOP中的几个基本概念,每次学习AOP都被这几个概念折腾的很不爽,我们在这里再把这几个概念描述一遍,力争把这几个概念搞清,在每次review这块内容的时候可以很快上手。

  1. 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
  2. 连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
  3. 通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
  4. 切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
  5. 目标对象(Target):就是被AOP动态代理的目标对象;

用一张图来形象地表达AOP的概念及其关系如下:


 

3.2 Spring AOP中切入点、通知、切面的实现

理解了上面的几个概念后,我们分别来看看Spring AOP是如何实现这些概念的;

  1. 切入点(Pointcut):它定义了哪些连接点需要被织入横切逻辑;在Java中,连接点对应哪些类(接口)的方法。因此,我们都能猜到,所谓的切入点,就是定义了匹配哪些娄的哪些方法的一些规则,可以是静态的基于类(方法)名的值匹配,也可以是基于正则表达式的模式匹配。来看看Spring AOP Pointcut相关的类图:


在Pointcut接口的定义中,也许你已经想到了,ClassFilter是类过滤器,它定义了哪些类名需要拦截;典型的两个实现类为TypePatternClassFilterTrueClassFilter(所有类均匹配);而MethodMatcher为方法匹配器,定义哪些方法需要拦截。

在上面的类图中:

  • StaticMethodMatch与DynamicMethodMatch的区别是后者在运行时会依据方法的参数值进行匹配。
  • NameMatchMethodPointCut根据指定的mappedNames来匹配方法。
  • AbstractRegexpMethodPointCut根据正则表达式来匹配方法。
  1. 通知(Advice):通知定义了具体的横切逻辑。在Spring中,存在两种类型的Advice,即per-class和per-instance的Advice。

所谓per-class,即该类型的Advice只提供方法拦截,不会为目标对象保存任何状态或者添加新的特性,它也是我们最常见的Advice。下面是per-class的类图:


  • BeforeAdvice:在连接点前执行的横切逻辑。
  • AfterReturningAdvice:在连接点执行后,再执行横切逻辑。
  • AfterAdvice:一般由程序自己实现,当抛出异常后,执行横切逻辑。
  • AroundAdvice:Spring AOP中并没有提供这个接口,而是采用了AOP Alliance的MethodInteceptor接口;通过看AfterReturningAdvice的源码我们知道,它是不能更改连接点所在方法的返回值的(更改引用);但使用的MethodInteceptor,所有的事情,都不在话下。

在上面的类图中,还有两种类没有介绍,那就是***AdviceAdapter和***AdviceInteceptor,我们以AfterReturningAdviceInterceptor为例来说明:


该类实现了MethodInterceptor和AfterAdvice接口,同时构造函数中还有一个AfterReturningAdvice实例的参数;这个类存在的作用是为了什么呢?对,没错,Spring AOP把所有的Advice都适配成了MethodInterceptor,统一的好处是方便后面横切逻辑的执行(参看下一节),适配的工作即由***AdviceAdapter完成;

哈哈,Spring AOP的代码也不过如此嘛:所谓的AfterReturningAdvice,通过适配成MethodInterceptor后,其实就是在invoke方法中,先执行目标对象的方法,再执行的AfterReturningAdvice所定义的横切逻辑。你现在明白它为什么不能修改返回值的引用了吧?

对于per-instance的Advice,目前只有一种实现,就是Introduction,使用的场景比较少,有兴趣的同学可以自己研究一下,呵呵!

  1. 切面(Aspect):在Spring中,Advisor就是切面;但与通常的Aspect不同的是,Advisor通常只有一个Pointcut和一个Advice,而Aspect则可以包含多个Pointcut和多个Advice,因此Advisor是一种特殊的Aspect。但,这已经够用了!

接下来看下per-class Advisor的类图:


其实没有什么好看的,前面已经说过,Advisor包含一个Pointcut和一个Advisor;在AbstractGenericPointcutAdvisor中,持有一个Advice的引用;下面的几个实现,均是针对前面提到的几种不同的Pointcut的实现。

 

3.3 Spring AOP实现的基本线索

我们选择ProxyFactoryBean作为入口点和分析的开始。ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,从中,可以看到一条实现AOP的基本线索。

所有的逻辑从以下的方法开始,我们主要针对单例的代理对象的生成:


下面我们深入到SpringAOP核心代码的内部,看看代理对象的生成机制,拦截器横切逻辑以及织入的实现。

 

3.4  代理对象的生成

对于getSingletonInstance()方法返回了什么,这就是代理对象如何产生的逻辑了,然我们须根溯源,看看传说中的proxy到底是如何一步一步的产生的。


ProxyFactoryBean是AdvisedSupport的子类,Spring使用AopProxy接口把AOP代理的实现与框架的其他部分分离开来。在AdvisedSupport中通过这样的方式来得到AopProxy,当然这里需要得到AopProxyFactory的帮助 ,从JDK或者cglib中得到想要的代理对象:


这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,它包含JDK和Cglib两种实现方式。让我接着往里面看:


可以看到其中的代理对象可以由JDK或者Cglib来生成,JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,我们进入JdkDynamicAopProxy实现中看看Proxy是怎样生成的:


用Proxy包装target之后,通过ProxyFactoryBean得到对其方法的调用就被Proxy拦截了, ProxyFactoryBean的getObject()方法得到的实际上是一个Proxy了,target对象已经被封装了。对 ProxyFactoryBean这个工厂bean而言,其生产出来的对象是封装了目标对象的代理对象。

 

3.5 拦截器的作用

前面分析了SpringAOP实现中得到Proxy对象的过程,接下来我们去探寻Spring AOP中拦截器链是怎样被调用的,也就是Proxy模式是怎样起作用的。
还记得在JdkDynamicAopProxy中生成Proxy对象的时候,有一句这样的代码吗?

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

这里我们的JdkDynamicAopProxy实现了InvocationHandler这个接口,this参数对应的是InvocationHandler对象,也就是说当 Proxy对象的函数被调用的时候,InvocationHandler的invoke方法会被作为回调函数调用:



上面所说的目标对象方法的调用,是通过AopUtils的方法调用,使用反射机制来对目标对象的方法进行的:


接下来,我们来看具体的ReflectiveMethodInvocation中proceed()方法的实现,也就是拦截器链的实现机制:


从上面的分析我们看到了Spring AOP拦截机制的基本实现,比如Spring怎样得到Proxy,怎样利用JAVA Proxy以及反射机制对用户定义的拦截器链进行处理。

 

3.6 织入的实现

在上面调用拦截器的时候,经过一系列的注册,适配的过程以后,拦截器在拦截的时候,会调用到预置好的一个通知适配器,设置通知拦截器,这是一系列Spring设计好为通知服务的类的一个,是最终完成通知拦截和实现的地方,例如对 MethodBeforeAdviceInterceptor的实现是这样的:


可以看到通知适配器将advice适配成Interceptor以后,会调用advice的before方法去执行横切逻辑。这样就成功的完成了before通知的织入。

  • AOP.zip (4.7 MB)
  • 下载次数: 45
分享到:
评论
1 楼 vanceinfo_xuefei 2012-12-03  
受益匪浅,既学习到aop的使用、原理,又学习到了怎么看源代码

相关推荐

    基于Springboot+Vue的墙绘产品展示交易平台毕业源码案例设计.zip

    网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。系统化是必要的,设计网上系统不仅会节约人力和管理成本,还会安全保存庞大的数据量,对于信息的维护和检索也不需要花费很多时间,非常的便利。 网上系统是在MySQL中建立数据表保存信息,运用SpringBoot框架和Java语言编写。并按照软件设计开发流程进行设计实现。系统具备友好性且功能完善。 网上系统在让售信息规范化的同时,也能及时通过数据输入的有效性规则检测出错误数据,让数据的录入达到准确性的目的,进而提升数据的可靠性,让系统数据的错误率降至最低。 关键词:vue;MySQL;SpringBoot框架 【引流】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    99-青海大学大数据中心建设分享.pptx

    99-青海大学大数据中心建设分享.pptx

    TD-LTE载波聚合方案.docx

    5G通信行业、网络优化、通信工程建设资料。

    10份网络优化创新案例.zip

    SA语音回落与切换流程冲突解决.pdf 计费模式错误导致SA语音承载建立失败,pdf BSF网元bug导致SA用户VOLTE业务故障,pdf SA基站SCTP偶联IP配置不规范导致切换失败的问题处理,pdf 第一医院SA+NSA双模基站方案保障5G查房车应用,pdf SA未配置互操作场景下终端语音业务研究案例,pdf SA站点天馈隔离度问题导致上行速率不及预期,pdf SA组网下微信小视频卡顿影响感知案例,pdf 基于八步法定位SA掉线问题.pdf SA站点测试宏微切换异常事件,pdf

    施工监理费计算依据.doc

    5G通信行业、网络优化、通信工程建设资料。

    wordpress插件WhatsApp右下角浮动悬浮客服按钮

    1、WhatsApp插件,可轻松实现wordpress后台设置,前台悬浮显示; 2、无缝集成:该插件将WordPress站点与WhatsApp无缝集成; 3、多人员支持:支持显示多个WhatsApp账户,让用户根据需求或偏好选择联系不同的团队成员 4、群组邀请:允许邀请用户加入特定的WhatsApp支持群组,便于群体咨询、公告发布或集体答疑。 5、响应式设计:插件具备响应式布局,确保在各种屏幕尺寸和设备类型上均能良好呈现并顺畅使用。 6、WooCommerce产品查询集成:针对电商网站,支持与WooCommerce产品查询功能结合,方便用户就具体商品提问。 7、带声音的自动弹窗:可设置带有声音提示的自动弹出窗口,提醒用户支持服务的存在 8、定制欢迎消息:设置个性化欢迎信息,向用户传递品牌关怀或引导其使用支持服务。 9、移动端与桌面端开关控制:可根据需要独立开启或关闭移动端或桌面端上的插件功能 10、GDPR合规:遵循欧盟GDPR数据保护法规,保障用户隐私及数据安全。

    基于YOLOv7的芯片表面缺陷检测系统

    目前随着电子领域的快速发展,芯片也已经成为日常生活中不可或缺的一部分。随着市场对芯片的需求不断增大,裸芯片表面缺陷检测任务的压力也越来越大。裸芯片表面的缺陷检测不仅能保证芯片成品的质量,而且有着统计缺陷数量,反馈给生产前道工序的重要意义,但是目前许多生产线对于裸芯片表面依旧采用人工目检的方法进行缺陷检测,不仅实时性差,耗时长,而且结果会受到检测人员主观因素的影响。  目前国内外的芯片表面缺陷检测设备不仅价格昂贵,而且功能比较单一,因此本文提出了一种基于深度学习的裸芯片表面缺陷检测算法,具有高效率,实时性好的特点,与传统人工目检的方式相比具有一定的优势

    基于SpringBoot的“大学生社团活动平台”的设计与实现.zip

    基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现

    英飞凌官方ADS库1.9.20版

    英飞凌官方ADS库1.9.20版

    汇编语言-assembly-贪吃蛇游戏-汇编语言期末大作业

    汇编语言——贪吃蛇游戏 GREEDY_SNAKE 是基于8086 汇编语言开发的,汇编语言风格是采用《汇编语言》第二版 王爽著; G_Snake.asm 本贪吃蛇游戏 实现了随机出现食物、统计分数、显示小蛇运动方向、响应键盘中断、指定方向自动移动、游戏结束恢复9h键盘中断和正常退出。 文件说明: 1. 安装DOSBOX:运行DOSBox0.74-win32-installer.exe即可安装; 2. 将Greedy_Snake clone到本地任意盘,eg:d:\Greedy_Snake - mount d:\Greedy_Snake 到一个指定虚拟盘符: - `mount k d:\Greedy_Snake` (why is k? because i like this charactor) 3. 运行G_Snake - 在DOSBOX的DOS提示符下键入: - `Z:\>K:`(回车) - `K:\>cd G_Snake`(回车) - 使用masm 5.0工具编译、链接、运行.asm源程序 - MASM.EXE、LINK.EXE、d

    物联网考试题库答案.doc

    5G通信行业、网络优化、通信工程建设资料

    基于Python的在线学习与推荐系统设计带vue前后端分离毕业源码案例设计.zip

    网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。系统化是必要的,设计网上系统不仅会节约人力和管理成本,还会安全保存庞大的数据量,对于信息的维护和检索也不需要花费很多时间,非常的便利。 网上系统是在MySQL中建立数据表保存信息,运用SpringBoot框架和Java语言编写。并按照软件设计开发流程进行设计实现。系统具备友好性且功能完善。 网上系统在让售信息规范化的同时,也能及时通过数据输入的有效性规则检测出错误数据,让数据的录入达到准确性的目的,进而提升数据的可靠性,让系统数据的错误率降至最低。 关键词:vue;MySQL;SpringBoot框架 【引流】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    考试资料+7、互联网与物联网.docx

    5G通信行业、网络优化、通信工程建设资料

    参考资料-人工智能对劳动力市场的影响机制研究.pdf

    参考资料-人工智能对劳动力市场的影响机制研究.pdf

    99-数据开放平台技术实现方案.pptx

    99-数据开放平台技术实现方案.pptx

    199-IBM数据治理新主张-数据治理及元数据管理.pptx

    199-IBM数据治理新主张-数据治理及元数据管理.pptx

    关注于一个操作系统框架的设计与实现。所使用的工具有gcc、nasm、bochs、gdb、vim等.zip

    【资源说明】【毕业设计】 1、该资源内项目代码都是经过测试运行成功,功能正常的情况下才上传的,请放心下载使用。 2、适用人群:主要针对计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、数学、电子信息等)的同学或企业员工下载使用,具有较高的学习借鉴价值。 3、不仅适合小白学习实战练习,也可作为大作业、课程设计、毕设项目、初期项目立项演示等,欢迎下载,互相学习,共同进步!

    自动驾驶中的数据闭环建立(一).pdf

    自动驾驶中的数据闭环链路的建立,数据驱动算法

    通信工程监理工作流程表.doc

    5G通信、网络优化与通信建设

    推荐智慧政务大数据 政务综合服务平台建设项目方案书.doc

    社会治理大数据平台建设项目的总体目标是以项目建设为契机,以“一个网络体系、一套应用系统、三个基础库”为依托,充分利用大数据挖掘、云计算等先进技术,有效整合各方信息资源,实现“人、地、物、事、组织”的网格化管理,从而带动XXX社会管理源头治理体系、动态协调机制、应急管理体制建设,实现XXX社会管理“精确化”、社会服务“人性化”,提升社会服务效能,并为XXX实现智慧城市奠定信息化基础。 主要建设目标是为政府社会管理良性有序运行提供基本手段和保证,促进政府对社会系统的组成部分、社会生活的不同领域以及社会发展的各个环节进行组织、协调、服务、监督和控制,整合政府各部门资源,实现统一运维管理,并建立安全和运维保障体系。科学划分网格单元,优化网格资源配置,构筑“区—街道—社区—网格”的四级管理架构,以社会管理、基层服务为核心,实现管理服务工作的全员化、精细化、信息化、实效化。

Global site tag (gtag.js) - Google Analytics