2019独角兽企业重金招聘Python工程师标准>>>

之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下JDK的动态代理。

基于这两者的实现上,这次来探索下Spring的AOP原理。虽然AOP是基于Spring容器和动态代理,但不了解这两者原理也丝毫不影响理解AOP的原理实现,因为大家起码都会用。

AOP,Aspect Oriented Programming,面向切面编程。在很多时候我们写一些功能的时候,不需要用到继承这么重的方法,例如对每个方法在执行前打log,在没有AOP的情况下,我们只能对每个方法都写一句打log的语句。如果是一个复杂点的功能,那么将会产生许多重复的代码,而且会对模块之间有更多的耦合。 然而,在AOP下,我们只需要通过特定的方法,就能直接切入代码,添加自定义的功能(后续再讲AOP里面的概念点)。

下面将从一个简单的示例入手,拆解示例的内容,通过源码分析,一步步带大家读懂AOP的原理实现。

使用示例

以下代码不以文字形式展示,若需要代码,可以到github查看完整Demo。 Demo:https://github.com/Zack-Ku/spring-aop-demo

Spring项目依然是用xml最原始的配置方式,为了只是能简单地阅读原理,否则会多很多自动配置的内容在里面。而AOP的配置用的是注解形式,因为毕竟看起来毕竟清晰,容易理解逻辑。

创建一个Gradle项目,添加对应的Spring与AOP的依赖。 (Gradle和Maven类似,都是自动化构建的工具。但与Maven相比,Gradle是基于groovy,采用DSL格式,具有更强的灵活性、简洁性、拓展性。现在连Spring的官方源码都是用Gradle的,可以说是一款面向未来的工具,后续也值得我们深入学习。)

创建一个Bean,TestBean。

创建AOP的Aspect。

然后写一个启动类,测试以上配置

运行结果:

com.zack.demo.TestBean.getStr()开始执行... getStr():Testing! com.zack.demo.TestBean.getStr()方法结束...

Demo:https://github.com/Zack-Ku/spring-aop-demo

示例解析与AOP术语概念

看到上面的结果,很容易猜想到,LogAspect作用了在TestBean上,使得每次执行TestBean上的方法时,都会执行对应的方法(before/after)。

LogAspect中带注解@Pointcut的allMethod(),是用来扫描程序中的连接点。当执行一个方法时,命中了连接点,则会根据不同的通知,执行对应的织入代码。在上面例子中,执行getStr()前会执行LogAspect中的before(),执行getStr()后会执行LogAspect中的after()。

具体的通知包含

  • @Before,前置通知,执行方法前执行
  • @AfterReturn,返回通知,正常返回方法后执行
  • @After,后置通知,方法最终结束后执行,相当于finaly
  • @Around,环绕通知,围绕整个方法
  • @AfterThrowing,异常通知,抛出异常后执行

开发者在命中连接点时,可以通过以上不同的通知,执行对应方法。这就是AOP中的Advisor。

以上的内容其实已经把AOP核心的概念都已经点出来了,我们再深入具体的认识下其中的术语,

  • Aspect,切面,一个关注点的模块。 例子中,LogAspect就是切面。
  • JoinPoint, 连接点,程序执行中的某个点,某个位置。 例子中,testBean.getStr()是连接点。
  • PointCut,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。 例子中,@PointCut注解就是切点表达式,匹配对应的连接点
  • Advice,通知,指在切面的某个特定的连接点上执行的动作。 例子中,before()与after()方法中的代码。
  • TargetObject,目标对象,指被切入的对象。 例子中,从ctx中取出的testBean则是目标对象。
  • Weave,织入,将Advice作用在JoinPoint的过程。

以上概念看起来可以还比较难懂,可以通过以下一图(来源于网络)来理解

请各位读者和各位程序员,在阅读源码的时候,一定要先搞清楚基本概念,和一定一定要知道对应概念的英文,否则在看源码的时候,根本对不上号,使理解难度大大提高。因为源码都是英文写的。

至此AOP的基本使用和概念相信大家都有一定的了解,下面开始从源码入手,去探索整个Spring AOP的实现。

源码分析

上面的例子之所以能完成AOP的代理,只因为Spring的xml配置里面加了这一句

< aop : aspectj-autoproxy / >

加上了这一个配置,使得整个Spring项目拥有了AOP的功能。全局搜索下aspectj-autoproxy这个字段,可以发现,是这个类AspectJAutoProxyBeanDefinitionParser解析了这个元素。

其中的parse方法调用的是AopNamespaceUtils类中的registerAspectJAnnotationAutoProxyCreatorIfNecessary。这个方法作用是初始化一个AOP专用的Bean,并且注册到Spring容器中。

解析这三个操作,

  1. 第一句,注册一个AnnotationAwareAspectJAutoProxyCreator(称它为自动代理器),这个Creator是AOP的操作核心,也是扫描Bean,代理Bean的操作所在。
  2. 第二句,解析配置元素,决定代理的模式。其中有JDK动态代理,还有CGLIB代理,这部分后续会再细讲。
  3. 第三句,作为系统组件,把Creator这个Bean,放到Spring容器中。让Spring实例化,启动这个Creator。

自动代理器

下面我们来细看AnnotationAwareAspectJAutoProxyCreator是怎么对Bean做AOP的。

AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator,里面实现了BeanPostProceesor接口的postProcessAfterInitialization方法(该方法在一个Bean加载到Spring后会执行)。

关联注释描述可知,当一个bean加载完后,执行了该方法,会生成一个新的代理对象,返回context中加载。

下面重点看其中的wrapIfNecessary方法。讲述了整个AOP的核心流程,是Spring AOP最最最核心的代码所在。

看到红框的两个核心方法,可以知道,先从刚加载的Bean中扫描出所有的advice和advisor,然后用它来创建一个代理对象。

获取Advisor

先看如何扫描出advice和advisor。 一步步Debug getAdvicesAndAdvisorsForBean(),找到BeanFactoryAspectJAdvisorsBuilder中的buildAspectJAdvisors方法。

该方法就是找出Spring容器中存在的AspectBean,然后返回所有AspectBean中的Advisor。

示例中,LogAspect就是AspectBean,然后LogAspect中的before和after方法就是Advisor。 所以最终返回了LogAspect中的Advisor(before和after)。

创建代理

拿到了所有的Advisor后,就进入了创建代理的流程了createProxy()。

这些入参,对比上一篇讲过的动态代理,其实非常相似。

  • beanClass,加载到Spring,触发AOP的bean类
  • targetSource,目标对象,示例中则是从ctx中取出的testBean
  • specificInterceptors,指定Advisor,示例中则是before和after的方法。

下面来具体看下代理的过程

代码可以概括为,创建一个proxyFactory对象,然后把上面的参数都丢到这个这个工厂里,最后从proxyFactory获取一个代理对象。

来看看ProxyFactory的getProxy方法是怎么生成代理对象的。

Debug该方法,可以在DefaultAopProxyFactory中createAopProxy看到

工厂会根据配置与目标对象的类型,选择用JDK动态代理(参考《你真的完全了解Java动态代理吗?看这篇就够了》)还是CGLIB的代理(CGLIB具体在后续讲)。

代理后的对象放回ctx中,然后当程序执行的时候,会直接调用这个代理类。

至此整个AOP的代理流程就结束了。下面来了解下CGLIG代理与JDK代理的不同

CGLIB与JDK代理区别

CGLIB(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

回顾下JDK代理,JDK代理需要一组需要实现的接口,然后通过这些接口获取构造方法,用这个构造方法和InvocationHandler,实例化一个对象出来。所以JDK的方式是基于接口的。

而CGLIB的代理是基于类的,用目标类生成一个子类,子类重写父类的方法,从而达到动态代理的效果。CGLIB的使用和实现等后面有机会再详细介绍。目前暂时只要理解两者不同的使用场景就足够了。

总结

回顾下Spring AOP的流程

  1. Spring加载自动代理器AnnotationAwareAspectJAutoProxyCreator,当作一个系统组件。
  2. 当一个bean加载到Spring中时,会触发自动代理器中的bean后置处理
  3. bean后置处理,会先扫描bean中所有的Advisor
  4. 然后用这些Adviosr和其他参数构建ProxyFactory
  5. ProxyFactory会根据配置和目标对象的类型寻找代理的方式(JDK动态代理或CGLIG代理)
  6. 然后代理出来的对象放回context中,完成Spring AOP代理

相信大家通过阅读本文,对Spring的AOP处理有一定的认识。想更深入地了解,探索每一步,每一行代码的实现,可以下载Demo源码,一步步地调试 Demo:https://github.com/Zack-Ku/spring-aop-demo


更多技术文章、精彩干货,请关注 博客:zackku.com wx公众号:Zack说码

转载于:https://my.oschina.net/zackku/blog/2875959

从源码入手,一文带你读懂Spring AOP面向切面编程相关推荐

  1. 一文带你读懂HTTP协议的前世今生

    点击上方蓝字关注我们 HTTP,Hypertext Transfer Protocol,超文本协议,是在万维网上传输文件(如文本.图形图像.声音.视频和其他多媒体文件)的规则集.如果web用户打开他们 ...

  2. DNN、RNN、CNN.…..一文带你读懂这些绕晕人的名词

    DNN.RNN.CNN.-..一文带你读懂这些绕晕人的名词 https://mp.weixin.qq.com/s/-A9UVk0O0oDMavywRGIKyQ 「撞脸」一直都是娱乐圈一大笑梗. 要是买 ...

  3. 一文带您读懂FCC、CE、CCC认证的区别

    一文带您读懂FCC.CE.CCC认证的区别 参考资料:https://3g.k.sohu.com/t/n411629823 FCC认证,CE认证,CCC认证是产品认证中比较常见的几个认证,前两者经常有 ...

  4. 机器学习中为什么需要梯度下降_机器学习101:一文带你读懂梯度下降

    原标题 | Machine Learning 101: An Intuitive Introduction to Gradient Descent 作者 | Thalles Silva 译者 | 汪鹏 ...

  5. 用程序员计算机算进制,一文带你读懂计算机进制

    hi,大家好,我是开发者FTD.在我们的学习和工作中少不了与进制打交道,从出生开始上学,最早接触的就是十进制,当大家学习和使用计算机时候,我们又接触到了二进制.八进制以及十六进制.那么大家对进制的认识 ...

  6. 一文带你读懂“经典TRIZ”

    本文承接上文<一文带第读懂TRIZ>,下面开始看第二个问题:什么是"经典TRIZ"? 很多书里都有对TRIZ的产生与发展的描述. 我个人在看了很多的书和文献以后,认为: ...

  7. 简单一文带你读懂Java变量的作用和三要素

    Java变量的作用 不只是java,在其他的编程语言中变量的作用只有一个:存储值(数据) 在java中,变量本质上是一块内存区域,数据存储在java虚拟机(JVM)内存中 变量的三要素 变量的三要素分 ...

  8. 一文带你读懂感知机的前世今生(上)

    一文带你读懂感知机的前世今生 前言 男女不分 什么是神经元 M-P神经元 全或无定律 McCulloch和Pitts 一种高度简化的模型 MP神经元和真值表 MP神经元的几何理解 后记 参考 前言 男 ...

  9. 带你读懂Spring 事务——事务的隔离级别(超详细,快藏)

    不了解事务的铁汁可以先看前两篇,讲的超详细,有问题还请您指点一二 带你读懂Spring 事务--认识事务 带你读懂Spring 事务--事务的传播机制(藏) 特别提示:本文所进行的实验都是在MySQL ...

  10. 带你读懂Spring Bean 的生命周期,嘿,就是玩儿~

    带你读懂Spring Bean 的生命周期,嘿,就是玩儿~ 一.前言 今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象.因为 Spring Bean 的 ...

最新文章

  1. 计算机桌面文件夹删除如何找回,电脑删除文件如何恢复 误操作的一剂后悔药...
  2. UICollectionView(一)基本概念
  3. 使用node和npmVS时出现的问题
  4. java计算并显示学生的成绩_Java开学测试-学生成绩管理系统
  5. 安装hadoop2.6.0伪分布式环境
  6. PHP 入门 - 2.函数
  7. Triangle Leetcode
  8. ninjala还是显示服务器维护,Ninjala更新2.0版本 调整了武器平衡以及BUG修复
  9. Android系统对话框
  10. Httpd2.4简介及CenOS6.6下编译安装
  11. 构建入门Restful Web服务
  12. java scanner以回车结束_大佬看了直呼内行,你当初Java刚入门是否也是这样写代码?...
  13. 电脑太慢了最简单的办法怎么弄_最简单快速的方法恢复电脑误删文件-数据恢复常见问题...
  14. GCC 4.9.0 公布,提升 C++11 和 C++14 特性
  15. oracle系统AP对应的凭证编号,AP主要账户及会计分录
  16. Android TextToSpeech(tts)语音播报(文字转语音)
  17. 算法设计与分析:Word Ladder(Week 4)
  18. [JAVA毕业设计]高速公路收费管理源码获取和系统演示
  19. java+selenium爬取图片签名
  20. 使用 openssl 创建自签发证书,含 IP证书 及 泛域名证书

热门文章

  1. 迁移cnblog博客
  2. C#复习(学生信息输入)
  3. Website English Comments
  4. jquery jqplot pierenderer 饼图百分比小于3的无法显示DataLabels
  5. WinForm中窗体重画成圆角矩形
  6. Amadeus Pro for Mac(多轨音频编辑器)
  7. Final Cut Pro X for Mac有哪些新功能?Final Cut Pro X Mac版怎么样?
  8. VEGAS Movie Studio 15 Platinum渲染选什么格式好?
  9. js或css指定元素点击时内容不可被选中
  10. Linux自建yum仓及相关知识详解