AOP实现原理
JDK代理原理
添加返回值
将方法只加载一次
代理对象也传进来
JDK
方法反射优化
-
前 16 次反射性能较低
-
第 17 次调用会生成代理类,优化为非反射调用
-
会用 arthas 的 jad 工具反编译第 17 次调用生成的代理类
为了把反射调用优化成正常调用 新建了一个代理类
cglib代理原理
和 jdk 动态代理原理查不多
-
回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
-
调用目标时有所改进,见下面代码片段
-
method.invoke 是反射调用,必须调用到足够次数才会进行优化
-
methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
-
methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
-
methodProxy创建过程 需要传的参数 后边一个是增强方法 一个是方法重载后的原始方法(继承)
创建MethodProxy 时 就会创建这两个fastclass类
-
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类
-
ProxyFastClass 配合代理对象一起使用, 避免反射
-
TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)
-
-
TargetFastClass 记录了 Target 中方法与编号的对应关系
-
save(long) 编号 2
-
save(int) 编号 1
-
save() 编号 0
-
首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号
-
然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射
-
-
ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法
-
saveSuper(long) 编号 2,不增强,仅是调用 super.save(long)
-
saveSuper(int) 编号 1,不增强, 仅是调用 super.save(int)
-
saveSuper() 编号 0,不增强, 仅是调用 super.save()
-
查找方式与 TargetFastClass 类似
-
-
为什么有这么麻烦的一套东西呢?
-
避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法
-
用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
-
-
调用invoke的时候 就会调用fastclass内部的invoke
核心是知道你的方法名通过创建methodproxy对象 传递给fastclass 在这个类里调用target对象的方法
创建代理对象的时候 因为是子类继承父类 所以会知道父类所有方法和参数,然后再代理类里面创建methodproxy对象 把每个方法和参数都传给这个对象 当创建这个对象的时候 就会动态生成两个类 :
ProxyFastClass 配合代理对象一起使用, 避免反射
TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)
这两个类把你methodproxy对象 传进来的方法和参数都 赋予一个唯一的编号 (通过最终调用getindex方法)
当你调用invoke的时候 你把对象和参数传进来 就会进入到
使用methodproxy的时候就已经 获得了它的匹配规则了。当你在外部调用invoke的时候 会根据你穿的方法参数 获得方法对应的编号 再调用内部的invoke方法 把那个编号传进去 内部就知道该调用对象的哪个方法了
执行这一步的时候 方法和编号就已经匹配上了
Spring选择代理的规则
这个就是Spring选择代理的方法
把目标类、切面、目标类是否实现接口 和这个proxyTargetClass的值 赋值给工厂 由工厂再生成 代理对象工厂内部封装了那两种(jdk cglib)的方法
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
根据切点表达式来判断你这个方法执行的时候要不要功能增强
切点匹配 有两种方式
匹配检验 检验一下是不是成功 成功了再进行代理
一种是根据路径
一种是根据注解
调用match方法进行匹配是否为切点
AspectJ的方法匹配有局限性
-
常见 aspectj 切点用法
-
aspectj 切点的局限性,实际的 @Transactional 切点实现
@Transcational 事务增强并没用以上两种方法 因为类、和接口也有可能加这个注解
而上面两种只能匹配方法。
它只能匹配方法 不能匹配 类 接口等
实现的原理类似下面
用这种方法可以实现解析类 和接口上的注解
Bean的一个后处理器AnnotationAwareAspectJAutoProxyCreator
创建代理对象的时候根据目标类 会在容器里找到和这个代理对象匹配的有资格的切面类,
同时也会把扫描到的高级切面转换为低级切面
知道切面之后应该就赋值给这个工厂 再由工厂创建代理对象
代理对象内部已经有重新写的invoke方法可以增强功能了(切面中包含了你需要增强的功能
直接就写到哪个handler类里了)
-
AnnotationAwareAspectJAutoProxyCreator 的作用
-
将高级 @Aspect 切面统一为低级 Advisor 切面
-
在合适的时机创建代理
-
以下是这个处理器的两个方法
-
findEligibleAdvisors 找到有【资格】的 Advisors
-
有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3
-
有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
-
-
wrapIfNecessary
-
它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
-
它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
-
这两个方法 第一个是找到容器中所有与Target 匹配的切面(调用target类的方法)
然后返回所有和他匹配的切面
第二个方法是 如果找到了 切面 返回值不为空就会创建代理对象
代理对象创建于初始化之后(正常顺序)
当存在循环依赖情况
这时候Bean1执行实例化后 在进行依赖注入的时候 需要bean2来注入 但是bean2还没创建 所以只能先去创建bean2 但是bean2还需要bean1的依赖注入 所以只能先创建bean1的代理对象给bean2注入 然后bean2初始化完成后 再给bean1进行依赖注入
这就是代理对象在依赖注入前被创建的情况
但是增强功能应该在初始化之后才增加 这样才有意义
切面的排序可以通过@Order注解来改变
但是@Order注解和@Bean一起用会失效
标记在方法上也失效
高级切面转换为低级切面过程
因为你高级切面可能有很多通知方法,低级切面只能一对一
所以需要转换
就是把切面里的通知方法解析出来
然后把你(增强的内容、切点(你需要增强的方法))和用factory创建通知对象
然后再把每一个低级的切面 封装成集合 就ok了
skr.~: 哈哈哈确实是写错了多谢指正
.苏北辰: 引用「(1*8^2+1*8^1+1*8^0)」 可以问下这串代表什么意思吗? (如果是八进制转换成十进制的话,是写错了吗?(小白,什么都不懂,在认真的表达自己的疑惑) [文章有帮到我,谢谢!]