此博文的编写,源于前段时间的惨痛面试经历。刚好近几天尘埃落定、手头事少,遂总结一二,与各位道友分享,欢迎吐槽指正。今年年初的这段面试经历,已于之前的博文中

整理发出(https://www.cnblogs.com/zzq6032010/p/10492109.html)。不会不丢人,但如果不会还不去整理总结、不去学习,这才是最丢人的!闲话少叙,下面开始正文。

注:本文是基于《Spring源码深度解析》(郝佳编著)一书梳理归纳而来,如果大家能结合Spring源码看,相信会了解更深刻。

零、概述

Spring的AOP实现原理是什么? 当有多个切面的切点切到同一个方法时,AOP是如何处理多个切点的调用顺序的?对于AOP的实现原理,想必大家都有过了解。 通过JDK或者

CGLIB动态代理创建指定方法的代理,执行方法时则根据切点匹配到对应的增强,执行之。但如果对源码有过了解,就会发现实际实现的过程复杂的多,远没有描述中的那么简单。

照例先粗略的罗列一下总流程:当多个切点切到同一个方法时,源码实现流程为:Spring容器启动时先注册AnnotationAwareAspectJAutoProxyCreator类的BeanDefinition(继承

自后处理器BeanPostProcessor),当程序开始调用实际的切面方法要生成bean实例时,会调用其postProcessAfterInitialization方法(对于BeanPostProcessor等后处理器的作用原

理详见另一篇博文 https://www.cnblogs.com/zzq6032010/p/10466378.html ),此方法创建代理替换了Bean实例 。在代理中包含了此方法的所有拦截器,当调用方法时,在代理的

invoke方法中,将拦截器封装进ReflectiveMethodInvocation(如果是CGLIB代理则是封装进CglibMethodInvocation),逐个调用其proceed方法实现增强方法的调用。

下面会按照以上流程的顺序,从注册AnnotationAwareAspectJAutoProxyCreator、创建AOP代理、调用目标方法这三个阶段详细讲述多个切点切同一个方法时AOP整个的流程。

      一、注册AnnotationAwareAspectJAutoProxyCreator                                                                                                              

在Spring源码中全局搜索启动AOP的自定义标签aspectj-autoproxy(<aop:aspectj-autoproxy>),可以定位到注册该自定义标签解析类的类AopNamespaceHandler。对自定义标

签有过了解的道友应该知道,该解析类的parse方法是解析的核心,代码如下所示:

 public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
AopNamespaceUtils.registerAtAspectJAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(registry, element);
return null;
}

可知,在注册的解析类AspectJAutoProxyBeanDefinitionParser的parse方法中,关键处是调用了AopNamespaceUtils的静态方法registerAspectJAnnotationAutoProxyCreatorIfNecessary。

此方法中完成了三个功能:

1、注册beanName为org.Springframework.aop.config.internalAutoProxyCreator、class为AnnotationAwareAspectJAutoProxyCreator的BeanDefinition

2、处理proxy-target-class(如果为true则使用cglib代理)跟expose-proxy(暴露当前的aop代理类,可用AopContext.currentProxy()获取)属性(此处对这两个属性的处理,是指以

key-value的形式放入BeanDefinition的propertyValues属性中)

3、注册组件并通知

此时,如果有多个切面类,Spring容器启动时会按照你配置的方式(在XML中以Bean标签的形式配置或者在切面类上加注解的方式)将这多个切面类作为BeanDefinition加载注册到容器中,

而是否有多个切面类对于此处解析并注册AnnotationAwareAspectJAutoProxyCreator这个BeanDefinition是没有影响的。

    二、创建AOP代理                                   

第一步中注册的类AnnotationAwareAspectJAUtoProxyCreator到底有何玄机,为何要注册它?且看看这个类的继承关系图:

可知此类继承了BeanPostProcessor,那么顺藤摸瓜查看其实现的方法postProcessorAfterInitialization。由之前的博文https://www.cnblogs.com/zzq6032010/p/10466378.html 可知,

此方法的执行时机是从Spring容器中getBean时初始化完Bean对象之后。外化到程序中,即当你要在程序中通过@Autowired等注解给成员变量进行依赖注入时执行。如果此时要依赖注入

的类中有被切面切到的PointCut,那么执行完postProcessorAfterInitialization方法后依赖注入的对象就是新生成的代理对象了。

追溯postProcessorAfterInitialization方法,可知关键点有两处:

1、getAdvicesAndAdvisorsForBean方法获取到所有增强,以Object[]的形式存放;

2、createProxy方法针对增强创建代理,最终postProcessorAfterInitialization方法返回的对象就是这个创建的代理对象,而此代理对象最后就成了getBean方法获取到的对象。

增强获取:

在AbstractAdvisorAutoProxyCreator类的aspectJAdvisorsBuilder.buildAspectJAdvisors()方法中,先获取所有的beanName,然后遍历beanNames,校验每一个beanName对应的type,

如果有AspectJ的注解,则通过advisorFactory.getAdvisor(factory)方法获取此切面类下的所有增强方法(先找到有Advice类注解(如@Before、@Around等)的方法,然后给每一个切点生成

对应PointCut对象,用InstantiationModelAwarePointcutAdvisorImpl统一封装,并对不同的PoinCut使用对应的增强器初始化(如@Before对应AspectJMethodBeforeAdvice)增强器),以

List<Advisor>形式存放。其中,切面中每个增强+对应的PointCut对应一个Advisor。

然后筛选获取到的所有增强器,只取到与当前bean相关的Advisor。相关方法为findAdvisorThatCanApply,其中过滤增强分了两种,一种是引介增强IntroductionAdvisor(类级别的拦截),

一种是普通的增强。

创建代理:

createProxy方法中,首先是创建了一个ProxyFactory,并对其进行了初始化,然后才是调用此代理工厂的getProxy方法获得代理对象。如果此处有多个Advisor,则将其添加到ProxyFactory

的List<Advisor>成员变量中。下面追溯代理对象的创建过程。

先创建了AopProxyFactory,又创建了AopProxy,最后通过getProxy方法获得代理对象。此处创建AopProxy时,会根据配置项或者代理类的特性选择是JdkDynamicAopProxy还是CglibProxy。

至于增强,则封装进了此代理对象的属性AdvisedSupport advised中。

三、调用目标方法

当调用第二步中获取到代理对象后,根据代理模式,我们知道程序会走invoke方法,就是在此方法中完成了对增强的调用。下面以JdkDynamicAopProxy为例,查看其invoke方法。

有两个重要的点:

1、对于exposeProxy的处理

当代理走到invoke方法时,如果之前解析到的exposeProxy为true,则通过AopContext.setCurrentProxy(proxy)将当前代理放入这个属性中,这样,我们在代码中使用AopContext.getCurrentProxy

才能获取到当前的代理对象。

2、拦截器链(即增强)的调用

在invoke方法中,将当前方法的所有拦截器都封装进ReflectiveMethodInvocation中,调用其proceed()方法,使所有拦截器生效。不同的增强器,如@Before、@After,他们的执行顺序由他们自身

功能来控制。

    总结: 

AOP的实现原理如上所述。对于一个切面中多个不同Advice的执行顺序,是由对应增强器的invoke方法本身实现的,具体顺序如下所示:

目标方法正常执行:@Around前 ->@Before ->执行方法 ->  @Around后 -> @After -> @AfterReturning

     目标方法抛异常:    @Around前 ->@Before ->方法报错 -> @After -> @AfterThrowing

对于多个切面类切同一个方法,哪个切面类中的增强器先执行?从上述AOP实现原理中可知AOP中没有规定不同切面的执行顺序,都是把切面打乱放进了List<Advisor>中,但从放入List中的顺序追溯,

可知对应的是Spring加载类后注册BeanDefinition的顺序,即Spring注册BeanDefinition的顺序。而此顺序有两个方法控制,一个是在类上加@Order(123)注解,后面的数字越小越早加载;另一个是实现

Ordered接口,重写getOrder方法,返回的值越小越早加载。好吧,追溯了一顿还是回到Spring中,哎,洗洗睡了。

AOP切面实现原理以及多个切面切同一个地方时的优先级讲解的更多相关文章

  1. Spring AOP基于配置文件的面向方法的切面

    Spring AOP基于配置文件的面向方法的切面 Spring AOP根据执行的时间点可以分为around.before和after几种方式. around为方法前后均执行 before为方法前执行 ...

  2. spring aop在mvc的controller中加入切面无效

    spring aop在mvc的controller中加入切面无效 因为MVC的controller,aop默认使用jdk代理.要使用cglib代理. 在spring-mybatis.xml配置文件中加 ...

  3. Spring框架IOC和AOP的实现原理(概念)

    IoC(Inversion of Control) (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控.控制权由应用代码中 ...

  4. Spring框架IOC和AOP的实现原理

    IoC(Inversion of Control) (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控.控制权由应用代码中 ...

  5. AOP基本概念、AOP底层实现原理、AOP经典应用【事务管理、异常日志处理、方法审计】

    1 什么是AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...

  6. 关于spring,IOC和AOP的解析原理和举例

    引用自:https://blog.csdn.net/paincupid/article/details/43152397 IOC:就是DAO接口的实现不再是业务逻辑层调用工厂类去获取,而是通过容器(比 ...

  7. Spring AOP 的实现 原理

    反射实现 AOP 动态代理模式实例说明(Spring AOP 的实现 原理)   比如说,我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,或部分监控.也许我们 ...

  8. 浅析Spring中AOP的实现原理——动态代理

    一.前言   最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...

  9. Spring技术内幕:Spring AOP的实现原理(一)

    一.SpringAOP的概述 1.AOP概念 AOP是Aspect-Oriented Programming(面向切面编程)的简称.维基百科的解释例如以下: Aspect是一种新的模块化机制,用来描写 ...

随机推荐

  1. abaqus修改inp直接建立工程

    前面已经知道,通过修改以下inp的节点和单元编号,就可以新建模型,可是对于大的工程来说,逐个选取单元进行添加材料以及确定哪步进行填土仍是比较麻烦的(如果工程网格划分好并告知哪些单元好属于哪些材料,哪些 ...

  2. nginx server

    配置nginx 首先apt install nginx 然后安装php apt-get install php7.0-fpm php7.0-mysql php7.0-common php7.0-mbs ...

  3. Vue下载页面显示内容

    摘抄自 https://www.cnblogs.com/zhangtianqi520/p/9323873.html 先下载所需依赖 npm install --save html2canvas npm ...

  4. POI对Excel的操作

    1. 先导包 commons-io-2.6.jar包,用于对文件的操作. 下载地址:http://commons.apache.org/proper/commons-io/download_io.cg ...

  5. ios uibutton加数字角标

    http://www.jianshu.com/p/0c7fae1cadac 第一种:https://github.com/mikeMTOL/UIBarButtonItem-Badge第二种:https ...

  6. 大数据开发主战场hive (企业hive应用)

    hive在大数据套件中占很的地位,分享下个人经验. 1.在hive日常开发中,我们首先面对的就是hive的表和库,因此我要先了解库,表的命名规范和原则 如 dwd_whct_xmxx_m 第1部分为表 ...

  7. 自兴人工智能 python特点了解

    计算机语言从语言执行分类来看,大概可分为编译型语言(如Java.c++)和解释型语言(如python.javascript) 1.编译型语言  java   c++ 编写源代码.java ---> ...

  8. day18_雷神_django第一天

    # django_day01 1.http 协议 超文本传输协议,HTTP有很多应用,但最著名的是用于web浏览器和web服务器之间的双工通信. 协议概述 HTTP是一个客户端终端(用户)和服务器端( ...

  9. Tmux会话的使用

    不想看废话的直接拖到下面看干货部分! 我们管理Linux服务器通常是通过ssh远程连接过去,如果在服务器上执行比较耗时的操作,比如下载安装软件.编译等等,如果需要数个小时来完成这些工作,但是又不得不关 ...

  10. BeanPostProcessor —— 连接Spring IOC和AOP的桥梁

    之前都是从大Boss的视角,来介绍Spring,比如IOC.AOP. 今天换个视角,从一个小喽啰出发,来加深对Spring的理解. 这个小喽啰就是, BeanPostProcessor (下面简称 B ...