Spring源码分析之 AOP(一,创建代理对象)

一,@EnableAspectJAutoProxy开启AspectJ自动代理

在这个注解中导入了一个@Import(AspectJAutoProxyRegistrar.class)类。这个类实现了ImportBeanDefinitionRegistrar(Bean定义的注册器),AspectJAutoProxyRegistrar实现了一个registerBeanDefinitions方法。通过AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);去获取名为AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"的Bean定义信息;然后帮我们导入AnnotationAwareAspectJAutoProxyCreator这个组件

二,AnnotationAwareAspectJAutoProxyCreator展开调查

1,如图可知,AnnotationAwareAspectJAutoProxyCreator这个类同时具有BeanFactory和BeanPostProcessor属性。点进去这个类,看看他们是怎么实现的,点进去发现AnnotationAwareAspectJAutoProxyCreator这个类本身并没有什么我们想要的,但是他有父类。一直点,找到最后一层父类AbstractAutoProxyCreator。

2通过查看,最终找到如下关系,由于AnnotationAwareAspectJAutoProxyCreator是AbstractAutoProxyCreator的子类,所以就找到AnnotationAwareAspectJAutoProxyCreator和BeanPostProcessor的关系,实现了一个后置处理器的前置方法。然后再找AnnotationAwareAspectJAutoProxyCreator和BeanFactroyAware的关系。AbstractAutoProxyCreator实现了postProcessBeforeInstantiation接口而postProcessBeforeInitialization是开放式接口。用于开发者自己实现

3,通过下面图可知,找到他们之间的关系

4,下面给出一张完整的关系图。

5,接下来就围绕这张关系图去找主要的类,找主要的方法去理清楚AOP的实现过程。由上图可知,一个最主要的类就是AbstractAutoProxyCreator,还有最主要的两个方法也就是那个后置处理器的前置方法和后置方法。postProcessBeforeInstantiation、postProcessAfterInitialization、下面一个一个进行分析。


三,postProcessBeforeInstantiation

一,找事务的增强器。

还记得之前的IOC的那篇文章在执行doCreateBean之前,resolveBeforeInstantiation这个方法将Bean定义信息进行了代理。并且放入了缓存中。postProcessBeforeInstantiation这个方法就是从resolveBeforeInstantiation这里方法里面走进来的。点进postProcessBeforeInstantiation这个方法,Object cacheKey = getCacheKey(beanClass, beanName);进来就去从缓存拿,然后看isInfrastructureClass这个方法,判断的是是不是基础的Bean。什么是基础Bean比如Advice.class\Pointcut.class\Advisor.class等等等。然后还要判断shouldSkip()这个方法应不应该跳过。点进去我们发现有一个这样的findCandidateAdvisors()方法,这个方法就是在找事务的增强器。那么为什么这么说,因为在父类里通过advisorRetrievalHelper(切面查找工具类)这个类的findAdvisorBeans方法可知并没有找到实现了Advisor.class的类。从上图也能看出AnnotationAwareAspectJAutoProxyCreator的类图里面也没有看到跟Advisor.class相关的类或者实现。这个方法是怎么判断的呢?它进来先是从缓存里面去拿,没拿到,紧接着去容器当中去找。BeanFactoryUtils.beanNamesForTypeIncludingAncestors,也没找到,所以直接返回了一个空的List对象。由此可知,我们可以把findCandidateAdvisors()方法理解为在查找事务的增强器。下面给出一张流程图便于理解。

二,寻找我们自己定义的AspectJ

findCandidateAdvisors()这个方法走完以后,没找到关于事务的切面,然后接着往下走this.aspectJAdvisorsBuilder.buildAspectJAdvisors()这段代码点进去,进去发现第一次this.aspectBeanNames获取依然是null,然后如果为空的话,通过BeanFactoryUtils.beanNamesForTypeIncludingAncestors这个东西,把所有的类的名称都捞出来。然后通过for循环你的BeanName去获取你的BeanType,然后判断你的beanType是不是AspectJ。如果 是接着往下走找到getAdvisors方法。第一步获取增强器的类型getAspectMetadata().getAspectClass()。 在获取增强器的名称。再往下看,有一个for循环,里面有一个getAdvisorMethods方法,也就是去循环你的这个方法,点到这个方法里面看一看,然后会发现,通过这个doWithMethods方法,会把你所有的增强器捞出来。并且要排除你的切点(Pointcut.class)。

三, 获取增强器getAdvisors

1首先循环你所有去除切点的增强器方法。没循环一次调用一次getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName)方法。点进getAdvisor方法。接下来找到一个创建切面的实现类InstantiationModelAwarePointcutAdvisorImpl(实例化切面),点进去,这个方法里前面做了一些基础赋值和判断是否是懒加载等校验。下面有个最关键的方法instantiateAdvice(this.declaredPointcut);点进去找到getAdvice方法(获取通知)。在这个方法中找到AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod)这个方法,他是意思是说,在方法上找到所有的切面上的注解。这个方法里会遍历如下图所示的类。如果找到了直接返回到getAdvice这个方法。

2,回到getAdvice方法中可以看到下面有一个switch选择。如下图,通过这switch判断你或取到的注解的类型是什么东东。如果是before就new一个AspectJMethodBeforeAdvice,这个东西就是切面通知。到这里直接就返回到getAdvisors方法中把它放入advisors中去。下面在画一张流程图

3,流程图

3,最终执行完,在buildAspectJAdvisors()方法中把刚才找到的切面都放入切面的缓存中去this.advisorsCache.put(beanName, classAdvisors)和this.aspectFactoryCache.put(beanName, factory);放到这两个缓存里面来。至此shouldSkip()方法执行完毕。最后发现 postProcessBeforeInstantiation这个东西他就是说将我们自己定义切面都给捞出来。并且放入增强器缓存中。


四:postProcessAfterInitialization创建代理对象。

点进去这个方法,也许有人会问这个方法是从哪里进来的。在分析IOC的时候,说到了创建属性的那个方法,在后面进行初始化的时候,执行了一些后置处理器postProcessAfterInitialization 这个方法就是从invokeInitMethods方法中的applyBeanPostProcessorsAfterInitialization方法进来的。

点进去wrapIfNecessary这个方法前面执行了一些判断找到getAdvicesAndAdvisorsForBean这个方法。找Advices类型的和Advisors这个类型,这个方法是找事务和AOP都是通过这个方法。因为事务和AOP的执行流程很相似,事务的流程在后面的文章会进行讲解。那么这个方法到底做了什么呢。看名知意。点进去发现了findEligibleAdvisors这个方法,找到一个合适的增强器。再点进去。看第一行findCandidateAdvisors()又执行了一次,所以里面流程不会走,而是直接从缓存中拿到了增强器。因为在before中我们创建过了。既然找到了那么应该怎么办呢,看下面的方法

List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

这个意思就是说找到我本类能用的。比如说你现在有两个切面,当本类是A的时候,那么只能有其中的一个切面对我进行增强的植入,那么另一个可能在切另外一个类,所以这个方法就是找出我奔雷能用的切面。点进去看看。然后找到方法canApply(candidate, clazz, hasIntroductions),意思是能用的。顺便强调一下,我们只看有用的代码边边角角这里不做介绍。点进canApply方法。在其中你会发现有一段advisor   instanceof PointcutAdvisor,PointcutAdvisor pca = (PointcutAdvisor) advisor;将获取到的进行转换一下看看是否能用。在点进去canApply,这里面是一个方法的匹配器。通过循环去匹配看看我们能不能用。最后执行完,直接返回到getAdvicesAndAdvisorsForBean这个方法中,这个方法会判断,如果找到空的就不需要代理。如果找到了下一步就通过wrapIfNecessary这个方法中的createProxy来进行代理。

2,createProxy

AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);暴露代理对象

意思就是:@EnableAspectJAutoProxy(exposeProxy =true)暴露代理对象

点进createProxy方法,找到buildAdvisors方法点进去,把返回的切面数组放入proxyFactory.最后找到getProxy这个方法点进去,其实这个方法就是真正的去代理的对象.找到createAopProxy这个方法.其中一段targetClass.isInterface() || Proxy.isProxyClass(targetClass)如果是接口,或者Proxy的类就用jdk去代理,否则用Cglib去代理或者强制指定Cglib去代理,否则就用jdk去代理.执行完返回来,找到getProxy方法,Proxy.newProxyInstance(classLoader, proxiedInterfaces,this);真正的去创建代理对象.

五,总结

创建AOP的代理对象主要就是如下几点流程,第一postProcessBeforeInstantiation这个后置处理器就是找到你所有的切面信息.并且放入缓存中去,postProcessAfterInitialization这个后置处理器就是创建我们的代理对象.其中的一些校验,细节,上面也有部分的讲解.AOP(创建代理对象)至此结束,下面附上一张整体的流程图.


上图是一张从寻找切面到代理切面的一个完整的流程图。

本节完...如有问题或者错误,请在下方留言.欢迎一起探讨和经验分享!!!

QQ:527324958    

微信:xikuang925661

作者:夕阳

java高级程序员技术交流群:加群时请备注加群即可

手写代不易。请加关注,谢谢。

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

推荐阅读更多精彩内容