跟着小马哥学系列之 Spring AOP( ReflectiveMethodInvocation/CglibMethodInvocation 详解)

学好路更宽,钱多少加班。 ——小马哥

版本修订

  • 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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿大叔文海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值