内容简介:之前基于这两者的实现上,这次来探索下Spring的AOP原理。虽然AOP是基于Spring容器和动态代理,但不了解这两者原理也丝毫不影响理解AOP的原理实现,因为大家起码都会用。AOP,Aspect Oriented Programming,面向切面编程。在很多时候我们写一些功能的时候,不需要用到继承这么重的方法,例如对每个方法在执行前打log,在没有AOP的情况下,我们只能对每个方法都写一句打log的语句。如果是一个复杂点的功能,那么将会产生许多重复的代码,而且会对模块之间有更多的耦合。 然而,在AOP
之前 《零基础带你看Spring源码——IOC控制反转》 详细讲了Spring容器的初始化和加载的原理,后面 《你真的完全了解 Java 动态代理吗?看这篇就够了》 介绍了下JDK的动态代理。
基于这两者的实现上,这次来探索下Spring的AOP原理。虽然AOP是基于Spring容器和动态代理,但不了解这两者原理也丝毫不影响理解AOP的原理实现,因为大家起码都会用。
AOP,Aspect Oriented Programming,面向切面编程。在很多时候我们写一些功能的时候,不需要用到继承这么重的方法,例如对每个方法在执行前打log,在没有AOP的情况下,我们只能对每个方法都写一句打log的语句。如果是一个复杂点的功能,那么将会产生许多重复的代码,而且会对模块之间有更多的耦合。 然而,在AOP下,我们只需要通过特定的方法,就能直接切入代码,添加自定义的功能(后续再讲AOP里面的概念点)。
下面将从一个简单的示例入手,拆解示例的内容,通过源码分析,一步步带大家读懂AOP的原理实现。
使用示例
以下代码不以文字形式展示,若需要代码,可以到github查看完整Demo。
Demo: github.com/Zack-Ku/spr…
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: github.com/Zack-Ku/spr…
示例解析与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容器中。
解析这三个操作,
- 第一句,注册一个AnnotationAwareAspectJAutoProxyCreator(称它为自动代理器),这个Creator是AOP的操作核心,也是扫描Bean,代理Bean的操作所在。
- 第二句,解析配置元素,决定代理的模式。其中有JDK动态代理,还有CGLIB代理,这部分后续会再细讲。
- 第三句,作为系统组件,把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的流程
- Spring加载自动代理器AnnotationAwareAspectJAutoProxyCreator,当作一个系统组件。
- 当一个bean加载到Spring中时,会触发自动代理器中的bean后置处理
- bean后置处理,会先扫描bean中所有的Advisor
- 然后用这些Adviosr和其他参数构建ProxyFactory
- ProxyFactory会根据配置和目标对象的类型寻找代理的方式(JDK动态代理或CGLIG代理)
- 然后代理出来的对象放回context中,完成Spring AOP代理
相信大家通过阅读本文,对Spring的AOP处理有一定的认识。想更深入地了解,探索每一步,每一行代码的实现,可以下载Demo源码,一步步地调试。
Demo: github.com/Zack-Ku/spr…
更多技术文章、精彩干货,请关注
博客:zackku.com
微信公众号:Zack说码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Music Recommendation and Discovery
Òscar Celma / Springer / 2010-9-7 / USD 49.95
With so much more music available these days, traditional ways of finding music have diminished. Today radio shows are often programmed by large corporations that create playlists drawn from a limited......一起来看看 《Music Recommendation and Discovery》 这本书的介绍吧!