切点、通知、切面
Spring 中对切点、通知、切面的抽象如下
- 切点:接口 Pointcut,典型实现 AspectJExpressionPointcut
- 通知:典型接口为 MethodInterceptor 代表环绕通知
- 切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut
代理相关类图
- AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
- AopProxy 通过 getProxy 创建代理对象
- 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
- 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor
两个切面概念
aspect =
通知1(advice) + 切点1(pointcut)
通知2(advice) + 切点2(pointcut)
通知3(advice) + 切点3(pointcut)
...
advisor = 更细粒度的切面,包含一个通知和切点
总结:一个aspect 其实底层最终会拆解为advisor,所以笔者认为对advisor的理解会使我们更好理解aspect
一般我们写的代码大概是这样的
advisor执行流程
准备好切点
切点在spring中用一个叫Pointcut的接口来表示
查看源码发现由对class进行匹配的与对方法进行匹配的抽象方法,其中也提供了很多的子实现
比如注解匹配的、AspectJ表达式匹配的,这里我们选后者为例
看完上面解释之后我们开正式创建切点:
- 创建切点对象
- 设置切点表达式
测试类如下:
准备好通知
spring中通知也有很多的实现,这里我们说一个基本、也是重要的一个通知,基本其他通知最终都会转换为这个通知来执行,这个通知的名字为MethodInterceptor,这里不要与前面的一个同名接口弄混了,注意为下图这个,spring用的是第三方 接口定义
这个接口里只有一个抽象方法,所以作为一个函数式接口(抽象方法里就可以写我们想要的增强功能,本质上属于一种环绕通知)
创建通知:
- 创建通知实例
- 增强逻辑、调用目标
准备好切面
这个比较简单吗,我们找一个比较简单的实现,这里在构造时传入切点与通知
创建代理
我们这里就不用与代理底层打交道了,用一个ProxyFactory来创建,这里ProxyFactory会根据情况选择JDK实现或者cglib的实现
情况分类:
- proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
- proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
- proxyTargetClass = true, 总是使用 cglib 实现
创建代理流程:
- 设置目标
- 加入切面(可选加入一个或多个)
- 创建代理对戏(调用ProxyFactory的getProxy方法可实现创建,这里我们转换一下类型,因为我们要调用测试类接口的方法)
调用一下方法测试一下:
预期foo方法增强,bar方法不增强,结果如下(第一个是我为了看代理是由什么实现的用的cglib增强):
情况分类(代理选择情况分类):
- proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
- proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
- proxyTargetClass = true, 总是使用 cglib 实现
没有设置这个的话默认为目标没有实现任何接口(即使实现了也会装作看不见)
设置之后就会判断是否实现接口,之后就会按照上面情况分类选择代理增强类型
设置proxyTargetClass 演示
有接口实现,proxyTargetClass = false —> 符合第一条,期望代理实现为jdk代理,结果如下: