Spring源码解析(四)-Spring中AOP原理

@[TOC] spring AOP----》 实例化+拦截
#在前几篇文章中,详细介绍了spring的bean初始化和实例化的过程,在了解了bean的实例化过程后,对于后面spring的知识了解就有了一个很好的基础。本文主要是介绍spring中一个重要的知识点:AOP切面,这个在我们的开发中运用非常广泛,像 日志切面,事物切面,缓存切面等功能都是在本文的基础上进行实现,所以本文主要会介绍AOP的入口和基本信息的初始化,和调用过程。
这里强调一下AOP的必要属性:
一个切面Advisor有两个属性构成:
切点PointCut:切面要增强的地方,一般是包的模糊匹配或者注解)
方法增强Advice:就是对拦截到的一组功能方法的增强实现)。
后面讲的所有前面都必要有这两个属性,实例化过程和调用过程都是遵循AOP基本过程,只是不同的切面,对于PointCut和Advice的实现不同。
相关概念:
1)连接点(Joinpoint)
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
2)切点(Pointcut)
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
3)增强(Advice)
增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
4)目标对象(Target)
增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
5)引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
6)织入(Weaving)
织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
a、编译期织入,这要求使用特殊的Java编译器。
b、类装载期织入,这要求使用特殊的类装载器。
c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
7)代理(Proxy)
一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
8)切面(Aspect) advisor
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
advisor: pointCut +advice

part1:AOP实例化入口

在spring中,AOP的入口有多个,这里介绍三种,但是最后只是有一个生效。
1.@EnableAspectJAutoProxy
在这里插入图片描述
通过扫描注解@EnableAspectJAutoProxy(proxyTargetClass =true,exposeProxy = true)注册了 AOP 入口类,具体看看注解里面的@Import(AspectJAutoProxyRegistrar.class)
在这里插入图片描述
在这个类中,注册了 AOP 入口类 AnnotationAwareAspectJAutoProxyCreator
设置了两个属性:
proxyTargetClass
true
1、目标对象实现了接口 – 使用 CGLIB 代理机制
2、目标对象没有接口(只有实现类) – 使用 CGLIB 代理机制
false
1、目标对象实现了接口 – 使用 JDK 动态代理机制(代理所有实现了的接口)
2、目标对象没有接口(只有实现类) – 使用 CGLIB 代理机制
exposeProxy
该属性设置代理对象是否需要暴露,说白了就是是否需要把代理对象设置到 ThreadLocal 中。
内部也会把入口类封装成beandefinition对象,然后交给spring去实例化。
2.基于 xml配置,实际上就是自定义标签解析,解析过程和前面文章介绍了bean实例化过程类似,请参照 component-scan 标签解析过程。最终也是完成 AOP 入口类的注册。
3.bean 实例化完成后,会判断该 bean 是否生成代理,AOP 的入口,这是一个 BeanPostProcessor 接口的运用,initializeBean 方法我们都知道是一个
bean 实例化完成后做的操作,而这个代理实例生成也是在 bean 实例化完成后做的操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当前方式寻找当前 bean 的切面简单来说就两步:
1、从 spring 中找所有的切面
从 spring 中找所有切面,先找到所有的 beanDefinition 对象对应的 beanName,
拿到对应的 Class 对象,判断该类上面是否有@Aspect 注解,如果有则是我们要找的,
循环该 Class 里面的除了@PointCut 注解的方法,找到方法上面的 Around.class,
Before.class, After.class, AfterReturning.class, AfterThrowing.class注解,并且把注解里面的信息,比如表达式,argNames,注解类型等信息封装成对象AspectJAnnotation,然后创建 pointCut 对象,把注解对象中的表达式设置到 pointCut对象中,然后就是创建 Advice 对象,根据不同的注解类型创建出不同的 Advice 对象,对象如下:AspectJAroundAdvice,AspectJAfterAdvice,
AspectJAfterThrowingAdvice,AspectJMethodBeforeAdvice,
AspectJAfterReturningAdvice最终把注解对应的Advice对象和pointCut对象封装成
Advisor 对象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2、找到拦截当前 bean 的切面
从收集到的所有切面中,每一个切面都会有 pointCut 来进行模块匹配,其实这个过程就是一个匹配过程,看看 pointCut 表达式中的内容是否包含了当前 bean,如果包含了,那么这个 bean 就有切面,就会生成代理。
在这里插入图片描述

part2:AOP执行过程

比如我们的PointCut是包的模糊匹配,就是在调用当前路径下的add开头的方法
userService.addUser()的时候,就会被切面拦截,调用到代理对象&Proxy implementuserService的addUser(){h.invoke}中的invoke方法,
在这里插入图片描述
这个invoke在spring中就是实现了InvocationHandler的类,就是JdkDynamicAopProxy的invoke
在这里插入图片描述
在这里插入图片描述
因为在实例化bean的时候,如果有代理的话,会生成对应的代理对象和代理工厂,所以在invoke中,首先会从代理工厂中拿实例化过程中封装的切面Advisor
在这里插入图片描述
进入获取Advisor内部,可以看到从代理工厂拿到切面后,进行了一系列的匹配和封装,最后返回和当前类的当前方法匹配的Advice数组,便于后面进行链式调用。
在这里插入图片描述
获取到匹配的Advice后,开始进行链式调用
在这里插入图片描述
进入链式调用过程内部:
在这里插入图片描述
可以看到执行MethodInterceptor的invoke的时候,会有多个实现,这里就会根据当前执行的advice的类型进行具体的advice增强操作
在这里插入图片描述
对于不同的advice,这里看两个例子,先看around的增强,我们知道around是环绕增强,就是说可以在具体业务方法的前面和后面进行增强,在around增强方法中,我们自己进行了火炬传递pjp.proceed();这个就是把执行传递到后面去,这是我们自己在增强方法中实现的
around增强方法例子:
在这里插入图片描述
around的advice调用实现
在这里插入图片描述
但是对于befor这种增强,火炬传递是spring实现advice的时候进行传递的,我们的增强方法中不会进行火炬传递
before增强方法实现
在这里插入图片描述
before的advice实现
在这里插入图片描述
当执行完所有的advice后,也就会执行业务代码,这样对业务代码就进行了所有的增强
在这里插入图片描述
对于上面的执行过程,这里总结一个执行过程时序图:
在这里插入图片描述
由于图比较大,所以截图是缩小版,如果需要原版,请加关注后私信!!!
后面还会持续更新spring相关源码讲解,敬请期待!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值