AOP实现原理

JDK代理原理

  

 添加返回值

 将方法只加载一次

代理对象也传进来 

JDK

方法反射优化

  1. 前 16 次反射性能较低

  2. 第 17 次调用会生成代理类,优化为非反射调用

  3. 会用 arthas 的 jad 工具反编译第 17 次调用生成的代理类

为了把反射调用优化成正常调用 新建了一个代理类

cglib代理原理

和 jdk 动态代理原理查不多

  1. 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor

  2. 调用目标时有所改进,见下面代码片段

    1. method.invoke 是反射调用,必须调用到足够次数才会进行优化

    2. methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)

    3. methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象

 methodProxy创建过程 需要传的参数 后边一个是增强方法 一个是方法重载后的原始方法(继承)

 创建MethodProxy 时 就会创建这两个fastclass类

  1. 当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类

    • ProxyFastClass 配合代理对象一起使用, 避免反射

    • TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)

  2. TargetFastClass 记录了 Target 中方法与编号的对应关系

    • save(long) 编号 2

    • save(int) 编号 1

    • save() 编号 0

    • 首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号

    • 然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射

  3. ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法

    • saveSuper(long) 编号 2,不增强,仅是调用 super.save(long)

    • saveSuper(int) 编号 1,不增强, 仅是调用 super.save(int)

    • saveSuper() 编号 0,不增强, 仅是调用 super.save()

    • 查找方式与 TargetFastClass 类似

  4. 为什么有这么麻烦的一套东西呢?

    • 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法

    • 用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死

  5.  调用invoke的时候 就会调用fastclass内部的invoke

核心是知道你的方法名通过创建methodproxy对象 传递给fastclass 在这个类里调用target对象的方法 

 创建代理对象的时候 因为是子类继承父类 所以会知道父类所有方法和参数,然后再代理类里面创建methodproxy对象  把每个方法和参数都传给这个对象  当创建这个对象的时候 就会动态生成两个类 :

ProxyFastClass 配合代理对象一起使用, 避免反射

TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)

这两个类把你methodproxy对象 传进来的方法和参数都 赋予一个唯一的编号  (通过最终调用getindex方法)

 当你调用invoke的时候 你把对象和参数传进来  就会进入到

使用methodproxy的时候就已经  获得了它的匹配规则了。当你在外部调用invoke的时候 会根据你穿的方法参数 获得方法对应的编号   再调用内部的invoke方法  把那个编号传进去 内部就知道该调用对象的哪个方法了

 执行这一步的时候 方法和编号就已经匹配上了

Spring选择代理的规则

这个就是Spring选择代理的方法

把目标类、切面、目标类是否实现接口 和这个proxyTargetClass的值 赋值给工厂 由工厂再生成 代理对象工厂内部封装了那两种(jdk cglib)的方法

 》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

根据切点表达式来判断你这个方法执行的时候要不要功能增强

切点匹配 有两种方式

匹配检验 检验一下是不是成功 成功了再进行代理

 一种是根据路径

一种是根据注解 

调用match方法进行匹配是否为切点

AspectJ的方法匹配有局限性

  1. 常见 aspectj 切点用法

  2. aspectj 切点的局限性,实际的 @Transactional 切点实现

@Transcational 事务增强并没用以上两种方法 因为类、和接口也有可能加这个注解

而上面两种只能匹配方法。

它只能匹配方法 不能匹配 类 接口等 

实现的原理类似下面

 用这种方法可以实现解析类 和接口上的注解

Bean的一个后处理器AnnotationAwareAspectJAutoProxyCreator

创建代理对象的时候根据目标类 会在容器里找到和这个代理对象匹配的有资格的切面类,

同时也会把扫描到的高级切面转换为低级切面

 知道切面之后应该就赋值给这个工厂 再由工厂创建代理对象

代理对象内部已经有重新写的invoke方法可以增强功能了(切面中包含了你需要增强的功能

直接就写到哪个handler类里了)

  1. AnnotationAwareAspectJAutoProxyCreator 的作用

    • 将高级 @Aspect 切面统一为低级 Advisor 切面

    • 在合适的时机创建代理

以下是这个处理器的两个方法

  1. findEligibleAdvisors 找到有【资格】的 Advisors

    • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3

    • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得

  2. wrapIfNecessary

    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理

    • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

这两个方法 第一个是找到容器中所有与Target 匹配的切面(调用target类的方法)

然后返回所有和他匹配的切面

第二个方法是 如果找到了 切面 返回值不为空就会创建代理对象

 

代理对象创建于初始化之后(正常顺序) 

当存在循环依赖情况

 这时候Bean1执行实例化后 在进行依赖注入的时候 需要bean2来注入 但是bean2还没创建 所以只能先去创建bean2  但是bean2还需要bean1的依赖注入 所以只能先创建bean1的代理对象给bean2注入 然后bean2初始化完成后 再给bean1进行依赖注入  

这就是代理对象在依赖注入前被创建的情况

但是增强功能应该在初始化之后才增加 这样才有意义

切面的排序可以通过@Order注解来改变 

但是@Order注解和@Bean一起用会失效

标记在方法上也失效

 高级切面转换为低级切面过程

因为你高级切面可能有很多通知方法,低级切面只能一对一

所以需要转换

就是把切面里的通知方法解析出来

然后把你(增强的内容、切点(你需要增强的方法))和用factory创建通知对象

然后再把每一个低级的切面 封装成集合 就ok了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值