【好好面试】手把手调试,教你分析Spring-Aop

【干货点】 此处是【好好面试】系列文的第11篇文章。看完该篇文章,你就能够了解Spring中Aop的相关使用和原理,而且可以轻松解答Aop相关的面试问题。更重要的是,不少人其实一看源码就头大,此次专门将我的阅读源码的整个调试过程一步步呈现出来,但愿对大家有必定的帮助。面试

上篇文章比较轻松诙谐的描述了Aop的由来和实际应用【传送门:mp.weixin.qq.com/s/tQLO-lF_H… 】,答应过你们要补充一篇相关原理分析的文章,该篇文章会从SpringAop作了什么、相关原理一步步铺开讲。ide

大前提

看完上篇文章都知道,我这边定义了一个切面函数

图片描述

该切面定义了PointCut、Advice ,以及JoinPoint,以后定义了业务类BuyService和业务类ChatService,接下来我会经过源码跟踪的模式讲解下SpringAop作了什么。源码分析

Spring Aop作了什么【开始源码跟踪阅读】

首先给出Main类post

2

能够看到我这里用的是AnnotationConfigApplicationContext,解释下学习

AnnotationConfigApplicationContext是一个用来管理注解bean的容器,因此我能够用该容器取得我定义了@Service注解的类的实例。ui

打断点后,启动程序,咱们能够看到TestDemo的实例在idea的表现是这样的idea

3

而BuyService的实例却不一样3d

4

咱们能够从看到BuyService是SpringCGLIB强化过的一个实例,那么问题来了代理

  • 为何BuyService被强化过而TestDemo没有?
  • SpringCGLIB又是什么?
  • Spring是在何时生成一个强化后的实例的?

带着这些疑问,让咱们一步步从Spring源码中找到答案。

为何BuyService被强化过而TestDemo没有?

这个问题比较简单,咱们能够看回上面我对切片的定义

5

能够从代码中看出,我定义的切点是*Service命名的类,而TestDemo很明显不符合这个设定,所以TestDemo逃过被强化的命运。

SpringCGLIB又是什么?

CGLIB其实就是一种实现动态代理的技术,利用了ASM开源包,先将代理对象类的class文件加载进来,以后经过修改其字节码而且生成子类。结合demo来解读即是SpringCGLIB会先将BuyService加载到内存中,以后经过修改字节码生成BuyService的子类,该子类即是强化后的BuyService,上文看到的强化后的实例即是该子类的实例。

Spring是在何时生成一个强化后的实例的?

这个便厉害了,首先,咱们要先从Spring如何加载切片入手。

【思考Time】 为何我会选择从切片入手呢?缘由很简单,Spring就是由于发现了切片,而且对切片进行解析后才知道了要强化哪些类。

6

切片的处理第一步即是要加上@Aspect注解,学过注解的都知道,注解的做用更多的是标志识别,也就是告诉Spring这个类要作相关特殊处理,所以咱们能够基于该认识,反调该注解使用的地方

7

能够从截图看出,我反调了@Aspect后定位到了AbstractAspectJAdvisorFactory类中的hasAspectAnnotation函数,而且携带参数clazz,所以我猜想该接口就是用来识别clazz是否使用了注解@Aspect的地方,因而我打上了断点,而且加了条件 clazz == AuthAspect.class ,从新启动后

8

咱们看到确实被断点到了,能够得出个人猜想是对的。 咱们先看下断点后作了什么事情,以后再看下具体是哪里进行了扫描。在断点处按F8继续往下走,最后发现

13

没错,能够看到最终是构建成了一个Advisor对象 ,而且放入了BeanFactoryAspectJAdvisorsBuilder中的advisorsCache中,这样意味着Spring最终会将使用了@Aspect注解的类构建成Advisor对象后保存进BeanFactoryAspectJAdvisorsBuilder.advisorsCache中。

接下来咱们看看具体是哪里进行了使用@Aspect注解的相关类的扫描,此次我断点的地方在BeanFactoryAspectJAdvisorsBuilder中的advisorsCache调用了put的地方。

【思考Time】 为何我会选择在advisorsCache调用了put的地方打断点呢?缘由很简单,由于咱们上面已经分析出@Aspect注解的类构建成Advisor对象后保存进BeanFactoryAspectJAdvisorsBuilder.advisorsCache中,而我经过反调知道put的地方只有一个,所以我能够判定在此处打断点能够知道到底哪里进行了扫描的操做。

14

经过打断点后我从idea的Frames面板中看到

19

没错,作了扫描@Aspect注解的扫描器是AbstractAutoProxyCreator类

11
12

咱们能够从中看到AbstractAutoProxyCreator最终实现了InstantiationAwareBeanPostProcessor接口。

【思考Time】 这个接口有什么做用呢?具体能够看我前阵子写的一篇文章:mp.weixin.qq.com/s/r2OEqsap6…

如今已经找到了扫描注解的地方,而且咱们也看到了最终是生成了Advisor对象 ,而且放入了BeanFactoryAspectJAdvisorsBuilder中的advisorsCache中,那么Spring是在何时生成强化后的实例的呢? 接下来个人切入点是AbstractAutoProxyCreator中的postProcessAfterInitialization接口。

【思考Time】 之因此会选择AbstractAutoProxyCreator为切入点,是由于经过命名能够看出这是SpringAop用来构建代理[强化]对象的地方,而且因为SpringCGLIB是先将目标类加载到内存中,以后经过修改字节码生成目标类的子类,所以我猜想强化是在目标类实例化后触发postProcessAfterInitialization的时候进行的。

所以我在postProcessAfterInitialization接口中作了断点,而且加了调试条件。

14

能够看到我这里断点到了ChatService这个类。

【思考Time】 为何专门断点ChatService这个类?之因此会专门定位这个类,由于个人切面的目标类就包含了ChatService,经过定位到该类,咱们能够一步步捕捉Spring的强化操做。

咱们能够看到,生成强化后的对象就藏在wrapIfNecessary中。

【思考Time】 为何我会知道是生成强化后的对象就藏在wrapIfNecessary中呢?由于我经过调试发现,在调用了wrapIfNecessary接口后,返回的对象是强化后的对象。

那么问题来了,为何Spring会知道ChatService类须要进行进行强化呢?咱们能够从wrapIfNecessary中走入更深一层,经过调试,能够看到

16

在此处会从advisorsCache中根据aspectName取出对应的Advisor。拿到Advisor后,即是进行过滤的地方了,经过F8日后走,能够看到过滤的地方在AopUtils.canApply接口中。

17

能够看到此处传进来的targetClass符合切面的要求,所以能够进行构建强化对象。 接下来让咱们看下真正产生强化对象的地方了

18

咱们能够看到在AbstractAutoProxyCreator的createProxy函数中看到,最后会构造出一个强化后的chatService。 那么createProxy又作了什么呢?经过断点一层层深刻后,发现最后会到达

18

经过源码分析,咱们发如今AbstractAutoProxyCreator构建强化对象的时候是调用了createAopProxy函数,重点来了,咱们能够看到针对targetClass,也就是ChatService作了判断,若是targetClass有实现接口或者targetClass是Proxy的子类,那么使用的是JDK的动态代理实现AOP,若是不是才会使用CGLIB实现动态代理。

那么JDK实现的动态代理和CGLIB实现的动态代理有什么区别吗? 首先动态代理能够分为两种:JDK动态代理和CGLIB动态代理。从文中咱们也能够看出,当目标类有接口的时候才会使用JDK动态代理,实际上是由于JDK动态代理没法代理一个没有接口的类。JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,而CGLIB是针对类实现代理,主要是对指定的类生成一个子类,而且覆盖其中的方法。

Aop实现机制之代理模式

原本想一篇文章说完源码跟踪分析Aop和Aop的实现机制代理模式,发现源码跟踪分析已经很占篇幅了,所以没办法只能再开一篇文章专门阐述Aop的实现机制代理模式,期待下篇文章。

文章总结

从上面的源码阅读而且分析能够看出

  • 强化后的ChatService实例是在ChatService实例化后产生的,也就是AbstractAutoProxyCreator.postProcessAfterInitialization后。
  • 之因此Spring可以识别的出来为何ChatService实例须要进行强化,是由于在这一步以前Spring先使用AbstractAutoProxyCreator扫描了使用注解@Aspect的类,而且构形成了Advisor对象后放入了advisorsCache中。
  • 从advisorsCache取出来后对ChatService类进行识别,使用的是AopUtils.canApply。识别经过后,便会走入AbstractAutoProxyCreator.createProxy函数中,从中构建真正的强化对象。
  • 在构建强化对象的时候,走的是DefaultAopProxyFactory.createAopProxy,而且会对目标类进行判断,若是targetClass有实现接口或者targetClass是Proxy的子类,那么使用的是JDK的动态代理实现AOP,若是不是才会使用CGLIB实现动态代理。

说两句

认识个人都知道,我写的文章都会花费大量时间整理相关资料,本身了解透彻后才敢写相关文章,目的很简单,我想经过这种方式学习到更多东西,而后用本身的话和理解进行输出。以前有朋友说我发文太慢,劝我能够发发热点文章,这样涨粉也快点,我拒绝了,我想保持本身的初衷,经过学习后才作产出,不想浪费时间在对本身没有成长的文章上。

感谢这么久了一直关注个人朋友,感谢没有由于个人产出慢而取关,但愿能够一块儿成长!!!

logo
相关文章
相关标签/搜索