0
点赞
收藏
分享

微信扫一扫

Spring AOP源码分析十

爱做梦的夏夏 2022-04-06 阅读 63
spring

上一节中,我们将getInterceptors()方法就分析完毕了,说白了就是将增强advisor转换为拦截器MethodInterceptor的流程。这个时候我们就要和之前分析的invoke()方法的流程串起来了,不知道大家还记不记得,那就是之前分析invoke()方法时,我们一共有两个地方的细节都给跳过去了,一个是获取拦截器链的过程,另外一个就是拦截器链的执行过程。在上一节的内容中,其实就是增强advisor转换为拦截器MethodInterceptor的过程,拦截器最终会被放入到拦截器链中,而拦截器链会先放入缓存,接着会作为结果返回,而对应的代码其实就是下边的这行代码:
在这里插入图片描述
通过上图可以看到,这个chain就是返回的拦截器链。那么获取到这个拦截器链chain后,该怎么进行处理呢?我们继续往下看,此时就会看到下边这块代码:
在这里插入图片描述
这里会来看一下拦截器链chain是否为空,如果拦截器链chain为空的话是一种处理,而不为空是另外一种处理。那我们先来看下,拦截器链chain为空的情况吧,如果chain为空,那么此时就会执行这行代码,如下图:
在这里插入图片描述
上边这行代码需要注意的地方是,在调用invokeJoinpointUsingReflection()方法时,入参有目标对象target、目标方法method、参数args。那么这些参数传递进去后,会做些什么呢?我们点开invokeJoinpointUsingReflection()方法,会看到这边的代码:
在这里插入图片描述
通过上边的代码,可以看到,这里其实直接通过反射调用了目标方法。关键代码其实就一行,那就是method.invoke(target, args)。那么这里到底是什么意思呢?我们来简单分析下,如果拦截器链为空,那么说明没有匹配上任何增强方法。那么这个时候就代表,在执行目标方法的时候,不需要执行任何增强逻辑,那么这个时候就是直接执行目标方法就可以了,所以这里就直接通过反射来调用目标方法了。那么接下来我们来分析下拦截器链chain不为空的情况,此时就会执行下边这块代码:
在这里插入图片描述
首先会执行第一行代码 MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain)。这行代码其实就是构建了一个ReflectiveMethodInvocation对象,构造方法的入参有目标对象target、目标方法method、参数args以及拦截器链chain等,对应的构造方法代码如下:
在这里插入图片描述
其实上边这个构造方法非常简单,主要就是初始化目标对象target、目标方法method、拦截器链interceptorsAndDynamicMethodMatchers等属性。所以拦截器链执行的关键,还是下一行代码,也就是下边这行代码:
在这里插入图片描述
可以看到,由于刚才构建的invocation是ReflectiveMethodInvocation类的实例,所以上边这行代码,其实就是调用了ReflectiveMethodInvocation类的proceed()方法。我们继续往下看,此时ReflectiveMethodInvocation类的proceed()方法代码如下:

public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		// 使用反射执行目标对象的目标方法,此时拦截器链都执行完了,但是还没有执行完毕,而此时拦截器链中的before增强已经执行完毕了
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			// 进行动态方法的匹配
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				// 如果匹配失败,那么就跳过这个拦截器并调用下一个拦截器链中的下一个
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			// 这里是核心方法,如果是普通拦截器,那么就直接执行
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

首先执行这个proceed()方法时,会执行下边这块代码:
在这里插入图片描述
可以看到,这里有一个布尔表达式,而布尔表达式中的interceptorsAndDynamicMethodMatchers其实就是刚才的拦截器链chain,那么this.interceptorsAndDynamicMethodMatchers.size() - 1这行代码的结果其实就是拦截器链中最后一个拦截器的下标,比如拦截器链中一共有5个拦截器的话,那么这行代码的结果就是4。那么布尔表达式中的另外一个变量currentInterceptorIndex又是多少呢?我们来看下currentInterceptorIndex的定义,代码如下:
在这里插入图片描述
我们可以看到,currentInterceptorIndex默认为-1,那也就是说刚开始进来的时候,这个布尔表达式的结果为false,是不满足的。
那这个条件什么时候能满足呢?其实就是当currentInterceptorIndex的值为拦截器链最后一个拦截器的下标时,才会满足,此时就会执行invokeJoinpoint()这行代码。那么invokeJoinpoint()这行代码究竟是干嘛的呢?此时不妨我们点进来看下,invokeJoinpoint()方法代码如下:
在这里插入图片描述
通过上图,我们可以看到,其实这里又直接调用了invokeJoinpointUsingReflection()来完成相应的功能,但是需要注意的是入参,我们可以看到这里分别将目标对象target、目标方法method、参数arguments也传递了进去。那么这个invokeJoinpointUsingReflection()方法需要这些参数,是用来干嘛的?我们接着往下看,invokeJoinpointUsingReflection()方法代码如下:
在这里插入图片描述
其实这里核心只有一行代码,那就是method.invoke(target, args),说白了就是通过反射来调用目标方法!让我们简单来总结一下,我们看下边这块代码:
在这里插入图片描述
也就是说,这块代码在满足条件的情况下,会来执行invokeJoinpoint()方法,而这个invokeJoinpoint()方法其实就是用来执行目标方法的。而需要满足的条件就是currentInterceptorIndex需要等于拦截器链的最后一个下标,并且这个currentInterceptorIndex默认为-1。那这个currentInterceptorIndex后边会自动变化吗?既然代码这样写的,那么说明一定会有地方来修改currentInterceptorIndex的值,所以我们继续往下分析代码,此时我们会看到下边这行代码:
在这里插入图片描述
通过上边的代码,我们可以看到,这里就会对currentInterceptorIndex进行++操作,并且需要注意的是这个++是在currentInterceptorIndex变量前边的,所以会先对currentInterceptorIndex执行++操作,也就是currentInterceptorIndex会由-1变为0,然后再使用这个currentInterceptorIndex变量。这里直接将currentInterceptorIndex作为拦截器链的下标,来从拦截器链interceptorsAndDynamicMethodMatchers中获取拦截器,此时拦截器下标currentInterceptorIndex为0,所以获取的是第一个拦截器。由于这里是++操作,所以如果是第二次来执行上边这行代码,那么currentInterceptorIndex就会由0变为1,这个时候就会获取拦截器链中第二个拦截器了。那获取到拦截器之后,接下来又该干嘛了呢?我们继续往下看,此时会看到这么一块代码,如下图:
在这里插入图片描述
我们可以看到,这里根据拦截器的类型分别进行了处理,第一种是将刚才获取的拦截器强转为interceptorOrInterceptionAdvice类型,第二种是将拦截器强转为MethodInterceptor类型。很明显就是两种不同的处理,那么这里会走哪种呢?根据上一篇的分析,应该会走第二种,也就是会将拦截器强转为MethodInterceptor类型。我们知道aspectJ的五种增强都是MethodInterceptor接口的实现类,所以强转为MethodInterceptor是最符合逻辑的。我们可以来看下aspectJ五种增强对应拦截器的类继承关系,就会发现其实这些拦截器和InterceptorAndDynamicMethodMatcher没啥关系,比如After增强对应的拦截器实现类AspectJAfterAdvice的类继承图如下:

在这里插入图片描述
从类继承图中可以看到,AspectJAfterAdvice和InterceptorAndDynamicMethodMatcher没有任何关系。所以经过我们的分析,接下来就该走下边这行代码了,我们看这里:
在这里插入图片描述
而上边这行代码,很明显调用的是拦截器的invoke()方法,就是之前我们分析Advice实现类时。我们知道aspectJ一共有五种增强,而这五种增强也都实现了MethodInterceptor接口的invoke()方法,当然其中有两种增强是后来包装为MethodInterceptor类型的,但是最后这五种增强确实都实现了invoke()方法。那这个时候就会按照拦截器链上的顺序,来执行拦截器中的逻辑了。我们这里就要开始分析拦截器的代码了,我们在LogAspect 切面中有五种增强,代码如下:

package com.younger.springcloud.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect implements Ordered {


    @Pointcut("execution(public * com.younger.springcloud.service.impl.UserServiceImpl.*(..))")
    public void pointcut() {

    }

    @Before(value = "pointcut()")
    public void before() {
        System.out.println("新增用户之前操作");
    }

    @AfterReturning(value = "pointcut()")
    public void afterReturning(JoinPoint point) {
        System.out.println("返回通知");
    }

    @After(value = "pointcut()")
    public void after(JoinPoint point) {
        System.out.println("后置通知");
    }

    @AfterThrowing(value = "pointcut()")
    public void afterThrowing(JoinPoint point) {
        System.out.println("异常通知");
    }

    @Around(value = "pointcut()")
    public void round(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕通知前");
        point.proceed();
        System.out.println("环绕通知后");
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

可以看到,我们的日志切面类中有@After、@AfterThrowing、@Around、@Before和@AfterReturning五种增强,aspectJ的五种增强就已经集齐了。我们继续分析下边的代码:
在这里插入图片描述
可以看到,这里直接调用了拦截器的invoke()方法,并且将this,说白了就是将ReflectiveMethodInvocation自己作为入参传递到了拦截器中,那么接下来就要来执行拦截器中的逻辑了。那现在问题来了,我们知道aspectJ一共有五种拦截器,上边这行代码执行的是哪个拦截器呢?我们都知道,对于AOP的增强来说,Before增强通常都是先开始执行的,那么上边即将执行的拦截器是不是Before增强对应的拦截器呢?我们来看下拦截器链的结构信息了,我们使用debug调试,此时拦截器链的debug结果如下:
在这里插入图片描述
我们可以看到,此时拦截器链中有6个拦截器,我们的aspectJ的增强只有5种,这里为什么有6个呢?其实第一个拦截器ExposeInvocationInterceptor,它主要是用于暴露拦截器链到ThreadLocal中,这样同一个线程下就可以来共享拦截器链了。我们继续往下看。此时我们发现,在拦截器链中,After拦截器AspectJAfterAdvice竟然在Before拦截器MethodBeforeAdviceInterceptor的前面!如下图:
在这里插入图片描述
按照AOP的规则,Before增强肯定是在After增强前执行的呀,为什么拦截器链中的顺序,Before增强竟然排到After增强的后边呢?其实拦截器链的执行顺序,并不是简单的将拦截器链进行for循环遍历,那这个拦截器链到底是怎么来执行的呢?我们接着往下分析,我们知道拦截器的下标currentInterceptorIndex默认值是-1,再执行过++currentInterceptorIndex操作后,值会由-1修改为0。所以接下来的操作,就会来获取拦截器链中下标0对应的拦截器,也就是第一个拦截器ExposeInvocationInterceptor,而这个ExposeInvocationInterceptor拦截器的代码如下:

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.interceptor;

import java.io.Serializable;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.Advisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.PriorityOrdered;

/**
 * Interceptor that exposes the current {@link org.aopalliance.intercept.MethodInvocation}
 * as a thread-local object. We occasionally need to do this; for example, when a pointcut
 * (e.g. an AspectJ expression pointcut) needs to know the full invocation context.
 *
 * <p>Don't use this interceptor unless this is really necessary. Target objects should
 * not normally know about Spring AOP, as this creates a dependency on Spring API.
 * Target objects should be plain POJOs as far as possible.
 *
 * <p>If used, this interceptor will normally be the first in the interceptor chain.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
@SuppressWarnings("serial")
public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {

	/** Singleton instance of this class. */
	public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();

	/**
	 * Singleton advisor for this class. Use in preference to INSTANCE when using
	 * Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.
	 */
	public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
		@Override
		public String toString() {
			return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
		}
	};

	private static final ThreadLocal<MethodInvocation> invocation =
			new NamedThreadLocal<>("Current AOP method invocation");


	/**
	 * Return the AOP Alliance MethodInvocation object associated with the current invocation.
	 * @return the invocation object associated with the current invocation
	 * @throws IllegalStateException if there is no AOP invocation in progress,
	 * or if the ExposeInvocationInterceptor was not added to this interceptor chain
	 */
	public static MethodInvocation currentInvocation() throws IllegalStateException {
		MethodInvocation mi = invocation.get();
		if (mi == null) {
			throw new IllegalStateException(
					"No MethodInvocation found: Check that an AOP invocation is in progress, and that the " +
					"ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " +
					"advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!");
		}
		return mi;
	}


	/**
	 * Ensures that only the canonical instance can be created.
	 */
	private ExposeInvocationInterceptor() {
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}

	@Override
	public int getOrder() {
		return PriorityOrdered.HIGHEST_PRECEDENCE + 1;
	}

	/**
	 * Required to support serialization. Replaces with canonical instance
	 * on deserialization, protecting Singleton pattern.
	 * <p>Alternative to overriding the {@code equals} method.
	 */
	private Object readResolve() {
		return INSTANCE;
	}

}

在这里插入图片描述
在这里插入图片描述
上边就是ExposeInvocationInterceptor拦截器的代码,我们知道这个invoke()方法的入参mi,其实就是ReflectiveMethodInvocation实例,而ReflectiveMethodInvocation中是包含了拦截器链的。这块代码的话比较常简单,就是将mi变量放入了ThreadLocal中,但它真正想要做的,其实是想将拦截器链放入到ThreadLocal中,这样同一个线程,就可以通过ThreadLocal来共享拦截器链了。
并且我们可以看到,将mi变量放入ThreadLocal之后,还调用了非常关键的一行代码,那就是mi.proceed(),我们知道这个mi变量其实就是ReflectiveMethodInvocation,那也就是说,现在需要调用ReflectiveMethodInvocation的proceed()方法了,也就是这块代码:
在这里插入图片描述
我们知道,其实调用拦截器的入口,就是上边的这块代码,说白了就是ReflectiveMethodInvocation的proceed()方法。但在这个ExposeInvocationInterceptor拦截器中,竟然又调用了ReflectiveMethodInvocation的proceed()方法,相当于又回到了调用拦截器的入口,而这样其实就达到了递归调用的效果。那么这次递归调用proceed()方法,又有什么特殊之处呢?其实之前在执行这个proceed()方法时,拦截器链下标currentInterceptorIndex的值,已经由默认值-1累加为0了,而区别就在于执行下边这行代码:
在这里插入图片描述
通过上边的代码,我们可以知道,在这次递归调用时,currentInterceptorIndex的值已经变为0了,那么此时执行到++操作时,下标currentInterceptorIndex的值就会再次进行++操作,也就是会由0变为1。那这个时候就简单了,由于此时拦截器链下标currentInterceptorIndex为1,所以此时,就会获取并执行拦截器链中下标为1的拦截器了。那拦截器链中下标为1的拦截器是哪个呢?我们看下边这张图:
在这里插入图片描述
我们可以看到,拦截器链中下标为1的拦截器,其实就是AspectJAfterThrowingAdvice了,也就是说下一个要调用的拦截器,其实就是AspectJAfterThrowingAdvice,我们来画张图总结下:
在这里插入图片描述
通过上图,我们可以看到,第一个调用的拦截器是ExposeInvocationInterceptor,这个ExposeInvocationInterceptor拦截器,会先将拦截器链暴露到ThreadLocal中。然后会递归调用ReflectiveMethodInvocation的proceed()方法,那么此时下标index就会执行++操作,从而下标index会由0变为1,这个时候,就会获取并执行拦截器链中下标为1的拦截器了,也就是拦截器AspectJAfterThrowingAdvice。我们知道,接下来就要来执行AspectJAfterThrowingAdvice拦截器了,那我们就直接来看下AspectJAfterThrowingAdvice的代码,如下图:
在这里插入图片描述
通过上图,我们可以看到,拦截器AspectJAfterThrowingAdvice中直接执行了mi.proceed()这行代码,而这个入参mi,我们知道其实就是ReflectiveMethodInvocation类的实例。所以mi.proceed()这行代码,其实就是用来递归调用ReflectiveMethodInvocation的proceed()方法的,说白了就是用来执行拦截器链中的下一个拦截器的。而除了mi.proceed()这行代码,我们可以看到,这个拦截器中还进行了try catch处理,并且在catch语句块中,还有一块逻辑。在catch语句块中,调用了shouldInvokeOnThrowing()方法进行判断。那这个shouldInvokeOnThrowing()方法到底是用来判断什么呢?我们来看下shouldInvokeOnThrowing()方法的注释,如下图:
在这里插入图片描述
通过上图,可以看到,这个注释说的非常清楚,说白了就是在 AspectJ 语义中,只有在抛出的异常是给定抛出类型的子类型时,才会执行AfterThrowing增强方法。那么这个shouldInvokeOnThrowing()方法满足后,就会来执行invokeAdviceMethod()方法,如下图:
在这里插入图片描述
从这个invokeAdviceMethod()方法的名字上来看,是“执行增强方法”的意思,那它到底是通过什么方式,来执行增强方法的呢?
我们可以进一步来看下invokeAdviceMethod()方法的代码,如下图:
在这里插入图片描述
在这里插入图片描述
通过上图,我们可以看到,其实invokeAdviceMethod()方法主要是通过调用invokeAdviceMethodWithGivenArgs()方法来完成功能的。而invokeAdviceMethodWithGivenArgs()方法中关键的其实就一行代码,那就是this.aspectJAdviceMethod.invoke()这行代码,说白了就是通过反射调用了增强方法,而这个aspectJAdviceMethod大家还记得吗?其实就是当时构建各种Advice增强时,传入的入参candidateAdviceMethod,我们看下边的代码:
在这里插入图片描述
上图中传入的这个入参candidateAdviceMethod,其实就是切面中,定义增强方法对应的method对象。而上边的5种Advice,其实都是AbstractAspectJAdvice的子类,所以最终都会调用父类AbstractAspectJAdvice的构造方法完成属性赋值,如下图:

在这里插入图片描述
可以看到,这个aspectJAdviceMethod属性,最终会被赋值为切面中定义的增强方法对应的method对象。对aspectJAdviceMethod属性赋值的过程,其实在之前获取拦截器链的流程中。我们再回到主线,我们看下边这块代码:
在这里插入图片描述
我们知道,这个AspectJAfterThrowingAdvice拦截器,会通过递归调用,来执行拦截器链中的下一个拦截器。而由于这里有try catch的处理,所以当执行拦截器链报错时,并且抛出的异常是指定抛出类型的子类型时,那么就会通过反射的方式,来执行切面中定义的AfterThrowing增强方法了。所以说AfterThrowing增强方法只有在有异常时才会执行的原因。我们接着往下分析,此时就会继续递归调用,其实也就意味着,会接着往下执行拦截器链中的拦截器。那么在递归调用时,接下来又会执行拦截器链中的哪一个拦截器呢?其实我们看下边这张图就知道了。
在这里插入图片描述
通过上图,可以看到,此时拦截器链下标currentInterceptorIndex再次通过++操作后,会由1变为2,而拦截器链中下标为2的拦截器就是AfterReturningAdviceInterceptor了,也就是说下一个要执行的拦截器就是AfterReturningAdviceInterceptor了。
可以用下边的一张图来表示:
在这里插入图片描述
通过上图,可以看到,首先调用ExposeInvocationInterceptor拦截器将拦截器链暴露到ThreadLocal中后,接着通过递归调用了AspectJAfterThrowingAdvice拦截器。而在AspectJAfterThrowingAdvice拦截器中,直接进行递归操作,而这次递归操作,调用的其实就是AfterReturningAdviceInterceptor拦截器了。所以现在看来,拦截器中最核心的操作,其实就是这个递归调用,通过这个递归调用,可以将这些拦截器串成一根链条。

举报

相关推荐

0 条评论