Spring AOP的设计

概述

Spring aop通过动态代理的方式实现,在使用spring-aop时,工程中会引入一个非spring的jar包aopalliance包,spring5中将aopalliance包中的class打进了spring-aop包中(为了减少依赖?),先看看aopalliance包中的内容:

Aopalliance包非常简单,只包括aop相关的几个接口,可将其中的接口分为两类:

1.实体域:AOP所操作的核心实体

实体域是AOP中的核心概念,核心接口是Advice,其中MethodInterceptor和ConstructorInterceptor是Advice的子接口

2. 会话域:AOP运行时的上下文相关数据

这类对象中最主要的接口是Joinpoint,Invocation以及MethodInvocation和ConstructorInvocation接口都是Joinpoint的子接口

Spring-AOP是aopalliance的实现,并对aopalliance做了加强,增加了另外的实体——PointCut,PointCut表示切入点,即在哪些类的哪些方法上使用Advice

Spring AOP的设计

Spring AOP提供了多种使用方式,常用的有注释和XML,但注解和XML是非常简单易用的上层API,我们先看一下Spring AOP的更底层的API PointcutAdvisor接口,写一个PointcutAdvisor的实现类:

这里省略了getter/setter。可以看到,需要实现三个方法,先实现getPointcut方法,它需要返回一个Pointcut接口的对象,我们这里返回单例对象即可,本例简单起见,不管是否是单例,能说明问题即可,Pointcut接口中有两个方法,分别用于决定某个类是否要使用AOP的getClassFilter方法和决定某个方法是否需要使用AOP的getMethodMatcher方法。在getClassFilter方法的实现上,本例判断如果switchOn字段的值为true且 class被@SwitchOn注解标记,则返回true,否则返回false;getMethodMatcher方法返回的MethodMatcher接口要复杂一点,它包含三个方法,其中isRuntime返回true时表示运行时决定是否代理,如果isRuntime为true,则运行时会调用带args参数的matches方法,否则调用不带args参数的matches方法,代码实现如下:

Pointcut接口遵循了单一职责原则和接口分离原则,它没有将ClassFilter接口和MethodMatcher接口混在一起。

接下来实现Advice接口,这里由创建SwitchableAnnotationPointcutAdvisor时通过setter方法的调用设置advice:

最后使用这个advisor,需要创建一个Advice,以打日志为例:

注意:MethodInterceptor是Advice的子接口。

最后定义一个@Log注解,用于表示打了@Log注解的方法才打日志:


不同的环境可设置不同的switchOn的值,最后在需要打日志的方法调用上加上@Log注解,同时在其类上加上@SwitchOn注解即可:

以上是Spring-AOP的底层API的使用示例,对于AOP的其它更高层次的API和简单的使用方式,最终都是转化成Advisor被执行的。根据spring的xml解析机制可以发现<aop:config/>标签实际上是向spring中注册了一个代理对象创建类AspectJAwareAdvisorAutoProxyCreator,关键代码:

AspectJAwareAdvisorAutoProxyCreator类是一个BeanPostProcessor,创建动态代理对象的入口在其postProcessAfterInitializing方法中:

postProcessAfterInitializing方法的具体实现在抽象类AbstractAutoProxyCreator中,AbstractAutoProxyCreator类中处理了AOP代理对象的创建相关的逻辑,而AbstractAdvisorAutoProxyCreator类是继承自AbstractAutoProxyCreator,封装了Advisor的使用相关的逻辑,具体类AspectJAwareAdvisorAutoProxyCreator对父类中的几个方法做了覆盖,加入AspectJPointcutAdvisor相关的特定逻辑逻辑。

在AbstractAdvisorAutoProxyCreator中,与advisor相关的核心代码如下:

其中包含findCandidateAdvisors和findAdvisorsThatCanApply两个方法,其中前者用于拿到所有的Advisor,后者判断当前bean是否需要当前Advisor提供的切面逻辑(通过PointcutAdvisor中的Pointcut或者IntroductionAdvisor中的ClassFilter)。findCandidateAdvisors方法的实现可以有多种策略,默认实现是从BeanFactory中获取所有的Advisor接口的实例。

对于@Aspect注解定义的AOP切面逻辑,需要在xml配置文件中配置支持aspectj代理的标签<aop:aspectj-autoproxy/>,根据前面的xml解析机制,可以找到解析aspectj-autoproxy标签的类,它实际上是向spring中注入了AnnotationAwareAspectJAutoProxyCreator:

AnnotationAwareAspectJAutoProxyCreator类是AspectJAwareAdvisorAutoProxyCreator类的子类,其中实现对@Aspect等注解的处理,AnnotationAwareAspectJAutoProxyCreator覆盖了findCandidateAdvisors方法,实现了注解相关的Advisor创建逻辑,即从BeanFactory中获取标识了@Aspect注解的bean,并创建出Advisor:

通过aspectJAdvisorsBuilder.buildAspectJAdvisors的调用中可以找到创建Advisor使用了ReflectiveAspectJAdvisorFactory类,以下创建Advisor的核心代码:

最终会使用InstantiationModelAwarePointcutAdvisorImpl作为对应的Advisor。

最后再看看代理对象的创建过程,找到AbstractAutoProxyCreator类中创建代理对象的方法createProxy方法:

在proxyFactory.getProxy方法中就进入到了创建动态代理的阶段:

这里以jdk动态代理为例,JdkDynamicAopProxy就是一个InvocationHandler的实现,通过此InvocationHandler创建jdk动态代理,JdkDynamicAopProxy类的invoke方法实现比较长,其关键代码如下:

简单点说就是通过Advisor创建出拦截器链,拦截器链由advice组成,再通过拦截器链创建MethodInvocation对象,并调用proceed方法执行拦截器链。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容