以前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的彻底了解Java动态代理吗?看这篇就够了》介绍了下JDK的动态代理。java
基于这二者的实现上,此次来探索下Spring的AOP原理。虽然AOP是基于Spring容器和动态代理,但不了解这二者原理也丝绝不影响理解AOP的原理实现,由于你们起码都会用。ios
AOP,Aspect Oriented Programming,面向切面编程。在不少时候咱们写一些功能的时候,不须要用到继承这么重的方法,例如对每一个方法在执行前打log,在没有AOP的状况下,咱们只能对每一个方法都写一句打log的语句。若是是一个复杂点的功能,那么将会产生许多重复的代码,并且会对模块之间有更多的耦合。
然而,在AOP下,咱们只须要经过特定的方法,就能直接切入代码,添加自定义的功能(后续再讲AOP里面的概念点)。git
下面将从一个简单的示例入手,拆解示例的内容,经过源码分析,一步步带你们读懂AOP的原理实现。程序员
如下代码不以文字形式展现,若须要代码,能够到github查看完整Demo。
Demo:https://github.com/Zack-Ku/sp...github
Spring项目依然是用xml最原始的配置方式,为了只是能简单地阅读原理,不然会多不少自动配置的内容在里面。而AOP的配置用的是注解形式,由于毕竟看起来毕竟清晰,容易理解逻辑。spring
建立一个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/sp...微信
看到上面的结果,很容易猜测到,LogAspect做用了在TestBean上,使得每次执行TestBean上的方法时,都会执行对应的方法(before/after)。网络
LogAspect中带注解@Pointcut的allMethod(),是用来扫描程序中的链接点。当执行一个方法时,命中了链接点,则会根据不一样的通知,执行对应的织入代码。在上面例子中,执行getStr()前会执行LogAspect中的before(),执行getStr()后会执行LogAspect中的after()。工具
具体的通知包含
开发者在命中链接点时,能够经过以上不一样的通知,执行对应方法。这就是AOP中的Advisor。
以上的内容其实已经把AOP核心的概念都已经点出来了,咱们再深刻具体的认识下其中的术语,
以上概念看起来能够还比较难懂,能够经过如下一图(来源于网络)来理解
请各位读者和各位程序员,在阅读源码的时候,必定要先搞清楚基本概念,和必定必定要知道对应概念的英文,不然在看源码的时候,根本对不上号,使理解难度大大提升。由于源码都是英文写的。
至此AOP的基本使用和概念相信你们都有必定的了解,下面开始从源码入手,去探索整个Spring AOP的实现。
上面的例子之因此能完成AOP的代理,只由于Spring的xml配置里面加了这一句
< aop : aspectj-autoproxy / >
加上了这一个配置,使得整个Spring项目拥有了AOP的功能。全局搜索下aspectj-autoproxy这个字段,能够发现,是这个类AspectJAutoProxyBeanDefinitionParser解析了这个元素。
其中的parse方法调用的是AopNamespaceUtils类中的registerAspectJAnnotationAutoProxyCreatorIfNecessary。这个方法做用是初始化一个AOP专用的Bean,而且注册到Spring容器中。
解析这三个操做,
下面咱们来细看AnnotationAwareAspectJAutoProxyCreator是怎么对Bean作AOP的。
AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator,里面实现了BeanPostProceesor接口的postProcessAfterInitialization方法(该方法在一个Bean加载到Spring后会执行)。
关联注释描述可知,当一个bean加载完后,执行了该方法,会生成一个新的代理对象,返回context中加载。
下面重点看其中的wrapIfNecessary方法。讲述了整个AOP的核心流程,是Spring AOP最最最核心的代码所在。
看到红框的两个核心方法,能够知道,先从刚加载的Bean中扫描出全部的advice和advisor,而后用它来建立一个代理对象。
先看如何扫描出advice和advisor。
一步步Debug getAdvicesAndAdvisorsForBean(),找到BeanFactoryAspectJAdvisorsBuilder中的buildAspectJAdvisors方法。
该方法就是找出Spring容器中存在的AspectBean,而后返回全部AspectBean中的Advisor。
示例中,LogAspect就是AspectBean,而后LogAspect中的before和after方法就是Advisor。
因此最终返回了LogAspect中的Advisor(before和after)。
拿到了全部的Advisor后,就进入了建立代理的流程了createProxy()。
这些入参,对比上一篇讲过的动态代理,其实很是类似。
下面来具体看下代理的过程
代码能够归纳为,建立一个proxyFactory对象,而后把上面的参数都丢到这个这个工厂里,最后从proxyFactory获取一个代理对象。
来看看ProxyFactory的getProxy方法是怎么生成代理对象的。
Debug该方法,能够在DefaultAopProxyFactory中createAopProxy看到
工厂会根据配置与目标对象的类型,选择用JDK动态代理(参考《你真的彻底了解Java动态代理吗?看这篇就够了》)仍是CGLIB的代理(CGLIB具体在后续讲)。
代理后的对象放回ctx中,而后当程序执行的时候,会直接调用这个代理类。
至此整个AOP的代理流程就结束了。下面来了解下CGLIG代理与JDK代理的不一样
CGLIB(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它能够在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
回顾下JDK代理,JDK代理须要一组须要实现的接口,而后经过这些接口获取构造方法,用这个构造方法和InvocationHandler,实例化一个对象出来。因此JDK的方式是基于接口的。
而CGLIB的代理是基于类的,用目标类生成一个子类,子类重写父类的方法,从而达到动态代理的效果。CGLIB的使用和实现等后面有机会再详细介绍。目前暂时只要理解二者不一样的使用场景就足够了。
回顾下Spring AOP的流程
相信你们经过阅读本文,对Spring的AOP处理有必定的认识。想更深刻地了解,探索每一步,每一行代码的实现,能够下载Demo源码,一步步地调试
Demo:https://github.com/Zack-Ku/sp...
更多技术文章、精彩干货,请关注
博客:zackku.com
微信公众号:Zack说码