Spring AOP中Joinpoint、MethodInvocation、Interceptor、MethodInterceptor理解

前言

我们已经能够知道了,代理对象创建好后,其实最终的拦截工作都是交给了MethodInvocation,JDK交给:ReflectiveMethodInvocation,CGLIB交给CglibMethodInvocation

备注:此处所说的MethodInvocation是AOP联盟包里的,也就是org.aopalliance.intercept.MethodInvocation
AOP联盟包里和cglib包里都有的叫:MethodInterceptor,不要弄混了。

org.aopalliance.intercept.Joinpoint

首先需要注意的是,一般我们会接触到两个Joinpoint

  1. org.aspectj.lang.JoinPoint:该对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,可以很方便的获得更多信息。(一般用于@Aspect标注的切面的方法入参里),它的API很多,常用的有下面几个:
    1. Signature getSignature(); :封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
    2. Object[] getArgs();:传入目标方法的参数们
    3. Object getTarget();:被代理的对象(目标对象)
    4. Object getThis();:该代理对象

备注:ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中

  1. org.aopalliance.intercept.Joinpoint是本文的重点,下面主要看看它的解释和相关方法:
// 此接口表示运行时的连接点(AOP术语)  (和aspectj里的连接点意思有点像)
public interface Joinpoint {

// 执行此拦截点,并进入到下一个连接点
Object proceed() throws Throwable;
// 返回保存当前连接点静态部分【的对象】。  这里一般指的target
Object getThis();
// 返回此静态连接点  一般就为当前的Method(至少目前的唯一实现是MethodInvocation,所以连接点得静态部分肯定就是本方法喽)
AccessibleObject getStaticPart();
}

org.aopalliance.intercept.Invocation

它的中文意思:祈祷; 乞求,它继承自Joinpoint
这个类没有同名的,只有的aopalliance里有。

// 此接口表示程序中的调用~
// 该调用是一个可以被拦截器拦截的连接点
public interface Invocation extends Joinpoint {
// 获得参数们。比如方法的入参们
Object[] getArguments();
}

org.aopalliance.intercept.MethodInvocation

接口到了这一层,就比较具象了。它表示方法的执行器,显然就是和Method方法有关喽

// 方法调用时,对这部分进行描述
public interface MethodInvocation extends Invocation {
// 返回正在被调用得方法~~~  返回的是当前Method对象。
// 此时,效果同父类的AccessibleObject getStaticPart() 这个方法
Method getMethod();
}
MethodInvocation`作为`aopalliance`里提供的最底层接口了。Spring提供了相关的实现,如下图:
![【小家Spring】探索Spring AOP中aopalliance的JoinpointMethodInvocationInterceptorMethodInterceptor..._# 享学Spring MVC_02](https://s2.51cto.com/images/blog/202107/09/8b58350249fa66faab77d87eb2bef504.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/format,webp/resize,m_fixed,w_1184)
Spring自己也定义了一个接口,来进行扩展和统一管理:`ProxyMethodInvocation
org.springframework.aop.ProxyMethodInvocation

这个接口是Spring提供的对aopallianceMethodInvocation的继承扩展接口

// 这是Spring提供的对MethodInvocation 的一个扩展。
// 它允许访问  方法被调用的代理对象以及其它相关信息
public interface ProxyMethodInvocation extends MethodInvocation {

// 返回代理对象
Object getProxy();

// 克隆一个,使用的Object得clone方法
MethodInvocation invocableClone();
MethodInvocation invocableClone(Object... arguments);

// 设置参数  增强器、通知们执行的时候可能会用到
void setArguments(Object... arguments);
// 添加一些属性kv。这些kv并不会用于AOP框架内,而是保存下来给特殊的一些拦截器实用
void setUserAttribute(String key, @Nullable Object value);
@Nullable
Object getUserAttribute(String key);
}

下面我们就是主菜了,Spring给我们提供的唯一(其实算唯二吧)的实现类,它执行着拦截的核心逻辑。会让所有的通知器都执行~~

ReflectiveMethodInvocation

Reflective中文意思:可被反射的

显然它作为实现类,需要实现包括父接口在内的所有的方法们。
它也是JdkDynamicAopProxy最终执行时候new出来的执行对象,话不多说,下面看看具体的逻辑吧~~

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

protected final Object proxy; // 代理对象
@Nullable
protected final Object target; // 目标对象
protected final Method method; // 被拦截的方法

protected Object[] arguments = new Object[0];
@Nullable
private final Class<?> targetClass;

@Nullable
private Map<String, Object> userAttributes;
protected final List<?> interceptorsAndDynamicMethodMatchers;

// currentInterceptorIndex初始值为 -1 
private int currentInterceptorIndex = -1;

// 唯一的构造函数。注意是protected  相当于只能本包内、以及子类可以调用。外部是不能直接初始化的此对象的(显然就是Spring内部使用的类了嘛)
//invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// proxy:代理对象
// target:目标对象
// method:被代理的方法
// args:方法的参数们
// targetClass:目标方法的Class (target != null ? target.getClass() : null)
// interceptorsAndDynamicMethodMatchers:拦截链。  this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)这个方法找出来的
protected ReflectiveMethodInvocation(
Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
// 找到桥接方法,作为最后执行的方法。至于什么是桥接方法,自行百度关键字:bridge method
// 桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法(子类实现父类的泛型方法时会生成桥接方法)
this.method = BridgeMethodResolver.findBridgedMethod(method);
// 对参数进行适配
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

@Override
public final Object getProxy() {
return this.proxy;
}
@Override
@Nullable
public final Object getThis() {
return this.target;
}
// 此处:getStaticPart返回的就是当前得method
@Override
public final AccessibleObject getStaticPart() {
return this.method;
}
// 注意:这里返回的可能是桥接方法哦
@Override
public final Method getMethod() {
return this.method;
}
@Override
public final Object[] getArguments() {
return this.arguments;
}
@Override
public void setArguments(Object... arguments) {
this.arguments = arguments;
}


// 这里就是核心了,要执行方法、执行通知、都是在此处搞定的
// 这里面运用 递归调用 的方式,非常具有技巧性
@Override
@Nullable
public Object proceed() throws Throwable {
//	currentInterceptorIndex初始值为 -1  如果执行到链条的末尾 则直接调用连接点方法 即 直接调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 这个方法相当于调用了目标方法~~~下面会分析
return invokeJoinpoint();
}

// 获取集合中的 MethodInterceptor(并且currentInterceptorIndex + 1了哦)
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

//InterceptorAndDynamicMethodMatcher它是Spring内部使用的一个类。很简单,就是把MethodInterceptor实例和MethodMatcher放在了一起。看看在advisor chain里面是否能够匹配上
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;

// 去匹配这个拦截器是否适用于这个目标方法  试用就执行拦截器得invoke方法
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 如果不匹配。就跳过此拦截器,而继续执行下一个拦截器
// 注意:这里是递归调用  并不是循环调用
return proceed();
}
}
else {
// 直接执行此拦截器。说明之前已经匹配好了,只有匹配上的方法才会被拦截进来的
// 这里传入this就是传入了ReflectiveMethodInvocation,从而形成了一个链条了
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}


// 其实就是简单的一个:method.invoke(target, args);
// 子类可以复写此方法,去执行。比如它的唯一子类CglibAopProxy内部类  CglibMethodInvocation就复写了这个方法  它对public的方法做了一个处理(public方法调用MethodProxy.invoke)
@Nullable
protected Object invokeJoinpoint() throws Throwable {
// 此处传入的是target,而不能是proxy,否则进入死循环
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

@Override
public MethodInvocation invocableClone() {
Object[] cloneArguments = this.arguments;
if (this.arguments.length > 0) {
// Build an independent copy of the arguments array.
cloneArguments = new Object[this.arguments.length];
System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
}
return invocableClone(cloneArguments);
}
@Override
public MethodInvocation invocableClone(Object... arguments) {
if (this.userAttributes == null) {
this.userAttributes = new HashMap<>();
}
try {
ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
clone.arguments = arguments;
return clone;
} catch (CloneNotSupportedException ex) {
throw new IllegalStateException(
    "Should be able to clone object of type [" + getClass() + "]: " + ex);
}
}
@Override
public void setUserAttribute(String key, @Nullable Object value) {
if (value != null) {
if (this.userAttributes == null) {
this.userAttributes = new HashMap<>();
}
this.userAttributes.put(key, value);
}
else {
if (this.userAttributes != null) {
this.userAttributes.remove(key);
}
}
}
@Override
@Nullable
public Object getUserAttribute(String key) {
return (this.userAttributes != null ? this.userAttributes.get(key) : null);
}
public Map<String, Object> getUserAttributes() {
if (this.userAttributes == null) {
this.userAttributes = new HashMap<>();
}
return this.userAttributes;
}

@Override
public String toString() {
// Don't do toString on target, it may be proxied.
StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: ");
sb.append(this.method).append("; ");
if (this.target == null) {
sb.append("target is null");
}
else {
sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
}
return sb.toString();
}

}

从这里我们需要注意到的是:ProxyMethodInvocationReflectiveMethodInvocation)是代理执行的入口。然后内部会把所有的 增强器 都拿出来 递归执行(比如前置通知,就在目标方法之前执行) **这就实现了指定次序的链式调用**

CglibMethodInvocation

它是继承自ReflectiveMethodInvocation,是CglibAopProxy自己使用的执行器。

	private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

private final MethodProxy methodProxy;

private final boolean publicMethod;

public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {

// 调用父类的构造  完成基本参数得初始化
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);

// 自己的个性化参数:

// 这个参数是子类多传的,表示:它是CGLIb拦截的时候的类MethodProxy
//MethodProxy为生成的代理类对方法的代理引用。cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
// 它有两个重要的方法:invoke和invokeSuper
this.methodProxy = methodProxy;
// 方法是否是public的  对应下面的invoke方法的处理 见下面
this.publicMethod = Modifier.isPublic(method.getModifiers());
}

@Override
protected Object invokeJoinpoint() throws Throwable {
// 如果是public的方法,调用methodProxy去执行目标方法
// 否则直接执行method即可
if (this.publicMethod) {
// 此处务必注意的是,传入的是target,而不能是proxy,否则进入死循环
return this.methodProxy.invoke(this.target, this.arguments);
} else {
return super.invokeJoinpoint();
}
}
}


org.aopalliance.intercept.MethodInterceptor

需要说明的cglib包里也存在一个MethodInterceptor,它的主要作用是CGLIB内部使用,一般是和Enhancer一起来使用而创建一个动态代理对象。

而本处我们讲到的 org.aopalliance.intercept.MethodInterceptor,那些@AspectJ定义的通知们(增强器们),或者是自己实现的MethodBeforeAdviceAfterReturningAdvice…(总是都是org.aopalliance.aop.Advice一个通知器),最终都会被包装成一个org.aopalliance.intercept.MethodInterceptor,最终交给MethodInvocation(其子类ReflectiveMethodInvocation)去执行,它会把你所有的增强器都给执行了,这就是我们面向切面编程的核心思路过程。


这里面其实有两个标记接口(没有任何方法):
顶层接口:Advice 中文意思:建议,忠告。 实现通知的方式可议是任何方式,比如Interceptors拦截器得方式
中间层接口:Interceptor 继承自Advice接口。它就是以拦截器方式去实现通知的效果

此处需要说明的是,Interceptor得子接口有两个:MethodInterceptorConstructorInterceptor,但是ConstructorInterceptor连Spring都没有提供实现类,因此本文不会讲述本接口。

// 从名字里都能看出来,它是通过拦截方法的执行来实现通知得效果的~~~~
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
// 可议在此方法里  在方法执行之前、之后做对应的处理。
// 需要执行的时候,调用invocation.proceed()方法即可
Object invoke(MethodInvocation invocation) throws Throwable;
}

Spring给我们提供的MethodInterceptor实现非常非常的多:

有很多我们非常熟悉的面孔。下面就抽出几个,简单的看看实现代码:

关于AspectJ切面相关的增强器

一共5个对应着AspectJ提供的那五个注解。每个注解都是一个最终被包装好的Advice(其实是个AspectJAfterAdvice) 此处知道就可,暂时此处不做详细介绍~~

MethodBeforeAdviceInterceptor

这个源代码很简单,就是一层代理。把MethodBeforeAdvice包装成了一个MethodInterceptor

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

private MethodBeforeAdvice advice;

public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 在目标方法执行之前,先执行advice得before方法~~~
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());

// 注意此处继续调用了 mi.proceed()。相当于去执行下一个增强器。类似于递归执行了,这样就行程了一个链式得调用执行
return mi.proceed();
}
}
AfterReturningAdviceInterceptor

显然它是对AfterReturningAdvice的一个包装

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

private final AfterReturningAdvice advice;
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
// 嗲用afterReturning,它是能够享受到返回值的
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}

}
ThrowsAdviceInterceptor

包装的ThrowsAdvice,实际处理起来稍微复杂点~~~

public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
...
}

上面三个拦截器,可议参考适配器:

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}	 
}
CacheInterceptor / AsyncExecutionInterceptor

这两个分别涉及到Spring的cache部分以及Async异步部分,这在讲述这块的时候会具体的进行分析。

总结

aopalliance属于AOP联盟,定义了一些标准。一共只几个接口,总结如下:

org.aopalliance.aop
  • Advice:通知的标记接口。实现可以是任意类型,比如下面的Interceptor
  • AspectException:所有的AOP框架产生异常的父类。它是个RuntimeException
org.aopalliance.intercept
  • Interceptor:它继承自Advice,它通过拦截器得方式实现通知的效果(也属于标记接口)
  • MethodInterceptor:具体的接口。拦截方法 (Spring提供了非常多的具体实现类)
  • ConstructorInterceptor:具体接口。拦截构造器 (Spring并没有提供实现类)
  • Joinpoint:AOP运行时的连接点(顶层接口)
  • Invocation:继承自Joinpoint。 表示执行,提供了Object[] getArguments()来获取执行所需的参数
  • MethodInvocation:(和MethodInterceptor对应,它的invoke方法入参就是它)表示一个和方法有关的执行器。提供方法Method getMethod() (Spring提供了唯一(唯二)实现类:ProxyMethodInvocation
  • ConstructorInvocation:和构造器有关。Constructor<?> getConstructor(); (Spring没有提供任何实现类)

这就是AOP联盟为我们提供的所有的类,它里面全部是接口(那个异常类除外),相当于它定义了一套AOP的标准类。Spring对核心的Method相关的拦截、执行器做了对应的实现。

注意,Spring的AOP实现并不依赖于AspectJ任何类,它自己实现了一套AOP的。比如它Spring自己提供的BeforeAdviceAfterAdvice都是对AOP联盟规范的标准实现。以及Spring自己抽象出来的对Advice的包装:org.springframework.aop.Advisor贯穿Spring AOP的始终

但是在当前注解驱动的流行下,基于POJO(xml方式)以及编程的方式去书写AOP代理,显得非常的繁琐。因此Spring提供了另外一种实现:基于AspectJ,到这才使用到了AspectJ的相关注解、以及类。

但是还需要说明一点:哪怕使用到了AspectJ的相关注解和类,但核心的AOP织入的逻辑,还都是Spring自己用动态代理去实现的,没用AspectJ它那种特殊的语法和特有的编译器

最后说一句,若在Spring AOP中想使用AspectJ的方式去实现(也是当下最流行的方式),必须导入Jar包:aspectjweaver-1.9.2.jar,而Spring的这个包org.springframework.aop.aspectj下面的所有类,都是专门为了使用@Aspect的方式去服务的,毕竟AOP功能是Spring自己实现的,而不是依赖于AspectJ这个组件的

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MethodInterceptor是CGLib提供的一个拦截器接口,它可以在执行目标方法前后进行一些额外的操作,比如记录日志、性能监控、事务管理等。下面是一个注解实现MethodInterceptor的例子。 首先定义一个注解@Interceptor,用来标识需要被拦截的方法: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Interceptor { } ``` 然后定义一个拦截器类MyInterceptor,实现MethodInterceptor接口: ```java public class MyInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { if (method.isAnnotationPresent(Interceptor.class)) { // 判断方法是否被注解@Interceptor标识 // 在目标方法执行前进行一些额外的操作 System.out.println("Before method invocation: " + method.getName()); // 执行目标方法 Object result = proxy.invokeSuper(obj, args); // 在目标方法执行后进行一些额外的操作 System.out.println("After method invocation: " + method.getName()); return result; } else { // 如果方法没有被注解@Interceptor标识,则直接执行目标方法 return proxy.invokeSuper(obj, args); } } } ``` 最后,在需要被拦截的方法上使用@Interceptor注解即可: ```java public class UserService { @Interceptor public void saveUser(User user) { // 保存用户信息 } } ``` 使用CGLib生成UserService的代理类,并调用代理类的saveUser方法: ```java public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MyInterceptor()); UserService userService = (UserService) enhancer.create(); userService.saveUser(new User()); } ``` 输出结果如下: ``` Before method invocation: saveUser After method invocation: saveUser ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值