学好路更宽,钱多少加班。 ——小马哥
版本修订
- 2021.5.19:去除目录
简介
大家好,我是
小马哥
成千上万粉丝中的一员!2019年8月有幸在
叩丁狼教育
举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在
多次学习
极客时间《小马哥讲Spring AOP 编程思想》
基础上形成的个人一些总结。希望能帮助各位小伙伴, 祝小伙伴早日学有所成。
类图
相关接口或者类介绍
Joinpoint
这个接口表示一个通用的运行时连接点(在AOP术语中)。运行时连接点是发生在静态连接点(即程序中的一个位置)上的事件。例如,调用方法(静态连接点)是运行时连接点。可以使用 getStaticPart() 方法通用地检索给定连接点的静态部分。在拦截框架的上下文中,运行时连接点是对可访问对象(方法、构造函数、字段)访问的具体化,即连接点的静态部分。它被传递给安装在静态连接点上的拦截器。
方法介绍
- proceed():进入链中的下一个拦截器。此方法的实现和语义取决于实际的连接点类型(请参阅子接口)
- getThis():返回保存当前连接点的静态部分的对象(如果可访问对象是静态的,则可以为空)
- getStaticPart():返回此连接点的静态部分。静态部分是一个可访问的对象,在该对象上安装了一系列拦截器
Invocation
对 Joinpoint 接口扩展,这个接口表示程序中的一个调用。调用是一个连接点,可以被拦截器拦截。主要是通过 getArguments () 方法以数组对象的形式获取参数。可以通过改变数组中的元素值来改变参数。
MethodInvocation
对 Invocation 接口扩展,方法调用的描述,在方法调用时提供给拦截器。方法调用是一个连接点,可以被方法拦截器拦截。主要是通过 getMethod() (获取被调用的方法。此方法是Joinpoint.getStaticPart()方法的友好实现(结果相同))获取 Method
ProxyMethodInvocation
对 MethodInvocation 接口的扩展,允许访问代理通过方法被调用。如果有必要(例如,如果调用目标返回本身),可以使用代理替换返回值。
方法介绍
- getProxy():获取代理
- invocableClone():创建此对象的克隆。 在 proceed() 方法调用之前完成克隆,每调用一次 proceed() 方法则克隆一次。
- invocableClone(Object… arguments):带有参数的克隆
- setArguments(Object… arguments):设置参数被用于此链中后续调用的任何 advice 。
- setUserAttribute(String key, @Nullable Object value):将具有给定值的指定用户属性添加到此调用中。这样的属性在AOP框架本身中没有使用。它们只是作为调用对象的一部分保存,用于特殊的拦截器。
- getUserAttribute(String key):返回指定用户属性的值。
ReflectiveMethodInvocation
Spring 实现了 AOP 联盟 MethodInvocation 接口,实现了扩展的 ProxyMethodInvocation 接口。使用反射调用目标对象。子类可以覆盖 invokeJoinpoint() 方法来更改此行为。可以使用 invocableClone() 方法克隆调用,重复调用proceed()(每个克隆一次)。还可以使用 setUserAttribute()/getUserAttribute() 方法为调用附加自定义属性。主要用于
JdkDynamicAopProxy
中
@Override
@Nullable
public Object proceed() throws Throwable {
// 如果是没有 advice 链,或者 advice 链已经调用完,则通过反射执行目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 动态匹配,如果匹配上执行 advice 的 invoke 方法并把 MethodInvocation 对象当成参数传过去,在 invoke 方法中执行 MethodInvocation#proceed 方法
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 匹配不上执行下一个拦截器
return proceed();
}
}
else {
// 执行 advice 的 invoke 方法并把 MethodInvocation 对象当成参数传过去,在 invoke 方法中执行 MethodInvocation#proceed 方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
CglibMethodInvocation
扩展 ReflectiveMethodInvocation 类以支持 CGLIB 代理主要是覆盖了 invokeJoinpoint() 方法,如果有 MethodProxy 对象,则通过调用 MethodProxy#invoke 方法否则通过反射调用 。主要用于
CglibAopProxy
中。在 proceed() 方法中有个细节,就是有异常的时候,如果是
RuntimeException
(运行时异常)类型的异常则抛出,如果不是(指检查类型的异常)则检查方法签名上是否声明了该异常类型,否则将抛出
UndeclaredThrowableException
。