警告
本文最后更新于 2020-12-02,文中内容可能已过时。
spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework 。
链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。
1 代理方法执行回顾
在代理对象方法被调用时,会获取到执行链,将其封装在 ProxyMethodInvocation 中,调用 proceed(),通过递归方式,依次调用执行链中的通知方法节点,最终会调用到代理方法,然后回转到调用处。回转过程中可能存在后置通知方法的节点调用,其顺序与前置的调用刚好相反。
多个切面通知方法,执行顺序实际上是环环相扣的,被代理方法在中间,与内存模型的栈有点类似,先进后出。
业务开发中我们自定义的切面共有五种,实际上并不是每种都常用。
列举这五种通知方法的MethodInterceptor实现类和对应注解:
MethodBeforeAdviceInterceptor –> @Before
AspectJAroundAdvice –> @Around
AspectJAfterAdvice –> @After
AspectJAfterReturningAdvice –> @AfterReturning
AspectJAfterThrowingAdvice –> @AfterThrowing
2 MethodBeforeAdviceInterceptor
MethodBeforeAdviceInterceptor.invoe() 中,封装了 MethodBeforeAdvice,在这个 Advice 中,通过调用 before(),来完成通知方法的调用。
首先会调用 before(),接着,调用 MethodInvocation.proceed()。继续向下递归,直到执行链的末尾,调用被代理方法,然后返回。
源码如下:
1
2
3
4
5
6
7
8
9
10
public class MethodBeforeAdviceInterceptor implements MethodInterceptor , BeforeAdvice , Serializable {
// 封装了前置通知方法的具体执行内容。
private final MethodBeforeAdvice advice ;
@Override
public Object invoke ( MethodInvocation mi ) throws Throwable {
// 调用advice.before(),执行内容封装的前置通知方法。调用MethodInvocation.proceed(),火炬传递。实际上就是一个递归调用。
this . advice . before ( mi . getMethod (), mi . getArguments (), mi . getThis ());
return mi . proceed ();
}
}
来到 before() :
1
2
3
4
5
6
7
8
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice , Serializable {
@Override
public void before ( Method method , Object [] args , @Nullable Object target ) throws Throwable {
// 前置增强知识调用切面通知方法,并未理会参数中传递的目标对象、参数列表、以及方法
invokeAdviceMethod ( getJoinPointMatch (), null , null );
}
}
参数 JoinPointMatch 暂且忽略。
继续往下跟踪源码:
1
2
3
4
5
6
7
8
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
protected Object invokeAdviceMethod (
@Nullable JoinPointMatch jpMatch , @Nullable Object returnValue , @Nullable Throwable ex )
throws Throwable {
// 获取到绑定参数,调用通知方法。
return invokeAdviceMethodWithGivenArgs ( argBinding ( getJoinPoint (), jpMatch , returnValue , ex ));
}
}
上面源码中有两个关键方法,invokeAdviceMethodWithGivenArgs()、argBinding()。它们的作用分别是执行通知方法、获取通知方法的参数。
由于五种拦截器调用和获取参数绑定的逻辑都是相同的方法,这两个方法放到后面列出。
3 AspectJAroundAdvice
进入 invoke() 源码:
1
2
3
4
5
6
7
8
9
10
11
12
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor , Serializable {
@Override
public Object invoke ( MethodInvocation mi ) throws Throwable {
ProxyMethodInvocation pmi = ( ProxyMethodInvocation ) mi ;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint ( pmi );
JoinPointMatch jpm = getJoinPointMatch ( pmi );
// 执行环绕通知方法,注意,这里相对AspectJAfterAdvice,多传递了了一个参数pjp,这里是一个MethodInvocationProceedingJoinPoint,
// 内部封装了代理调用对象ProxyMethodInvocation。也就是说,在环绕通知方法中,是可以拿到ProxyMethodInvocation,并可以在其方法任意位置
// 进行执行链的火炬传递。
return invokeAdviceMethod ( pjp , jpm , null , null );
}
}
来到 lazyGetProceedingJoinPoint():
1
2
3
4
5
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor , Serializable {
protected ProceedingJoinPoint lazyGetProceedingJoinPoint ( ProxyMethodInvocation rmi ) {
return new MethodInvocationProceedingJoinPoint ( rmi );
}
}
在 new MethodInvocationProceedingJoinPoint(rmi)
参数中,传递了一个 ProxyMethodInvocation,这个类是用来调用执行链中通知方法的。这里把它封装到了ProceedingJoinPoint中。
在 invokeAdviceMethod() 中将这个创建的 ProceedingJoinPoint 传递下去。我们在使用 @Around 时,会将 ProceedingJoinPoint 作为通知方法的参数,这个参数就来自这里。
接着,会执行到 invokeAdviceMethod():
1
2
3
4
5
6
7
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
protected Object invokeAdviceMethod ( JoinPoint jp , @Nullable JoinPointMatch jpMatch ,
@Nullable Object returnValue , @Nullable Throwable t ) throws Throwable {
// 获取到绑定参数,调用通知。
return invokeAdviceMethodWithGivenArgs ( argBinding ( jp , jpMatch , returnValue , t ));
}
}
最终,这里有两个方法调用,invokeAdviceMethodWithGivenArgs()、argBinding()。
4 AspectJAfterAdvice
invoke() 源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor , AfterAdvice , Serializable {
@Override
public Object invoke ( MethodInvocation mi ) throws Throwable {
try {
// 先执行执行链,当所有执行链中的通知方法,及被代理方法执行完毕,会继续往下走。
// 最终,在finally语句块中,调用后置通知方法。卸载finally语句块里,是让在执行链中及被代理方法执行完毕,但还未返回时,执行后置通知方法。
// 这里有点绕。 A(around) -> B(after) -> C(around),假设有这三个切面组成执行链,他们执行顺序按照前面的排序。
// 执行顺序是这样的:A环绕前置通知 -> B invoke中通知调用执行链下一个节点(这里没有实际的执行内容,只是做一个火炬传递) -> C环绕前置通知 -> C环绕后置通知 -> B后置通知(这时候B中调用完,返回前掉finally语句块) -> A环绕后置通知。
return mi . proceed ();
}
finally {
invokeAdviceMethod ( getJoinPointMatch (), null , null );
}
}
}
先将执行链中剩余节点执行完毕,在执行完之后,方法返回之前,finally块中调用了后置通知方法。仔细看下源码中注释部分。
接着到 invokeAdviceMethod(),与前面的一致,都是到这个方法:
1
2
3
4
5
6
7
8
9
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
protected Object invokeAdviceMethod (
@Nullable JoinPointMatch jpMatch , @Nullable Object returnValue , @Nullable Throwable ex )
throws Throwable {
// 获取到绑定参数,调用通知方法。
return invokeAdviceMethodWithGivenArgs ( argBinding ( getJoinPoint (), jpMatch , returnValue , ex ));
}
}
5 AfterReturningAdviceInterceptor
先调用执行链中剩余节点,拿到返回值之后,再调用 afterReturning() 通知方法。afterReturning() 中一样会调到 invokeAdviceMethod()。
1
2
3
4
5
6
7
8
9
10
public class AfterReturningAdviceInterceptor implements MethodInterceptor , AfterAdvice , Serializable {
@Override
public Object invoke ( MethodInvocation mi ) throws Throwable {
// 首先执行执行链方法,当执行链中的通知方法、及被代理方法都执行完毕。才执行afterReturning方法。
Object retVal = mi . proceed ();
// 执行afterReturning通知方法。
this . advice . afterReturning ( retVal , mi . getMethod (), mi . getArguments (), mi . getThis ());
return retVal ;
}
}
afterReturning() 源码:
1
2
3
4
5
6
7
8
9
public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice
implements AfterReturningAdvice , AfterAdvice , Serializable {
@Override
public void afterReturning ( @Nullable Object returnValue , Method method , Object [] args , @Nullable Object target ) throws Throwable {
if ( shouldInvokeOnReturnValueOf ( method , returnValue )) {
invokeAdviceMethod ( getJoinPointMatch (), returnValue , null );
}
}
}
这里会传入 returnValue。
6 AspectJAfterThrowingAdvice
调用执行链中剩余的节点通知,当这些通知方法抛出异常,就执行当前通知方法。invokeAdviceMethod() 中会调用 invokeAdviceMethod()。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
implements MethodInterceptor , AfterAdvice , Serializable {
@Override
public Object invoke ( MethodInvocation mi ) throws Throwable {
try {
// 调用执行链,如果执行链中的剩余节点中,有任何一个节点抛出异常,在这里捕获,在catch语句块传递给afterThrowing通知方法。
return mi . proceed ();
}
catch ( Throwable ex ) {
if ( shouldInvokeOnThrowing ( ex )) {
invokeAdviceMethod ( getJoinPointMatch (), null , ex );
}
throw ex ;
}
}
}
这里传入了异常对象。
7 ArgBinding() 参数绑定
以下是 argBinding() 源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
protected Object [] argBinding ( JoinPoint jp , @Nullable JoinPointMatch jpMatch ,
@Nullable Object returnValue , @Nullable Throwable ex ) {
// 计算参数绑定
calculateArgumentBindings ();
// AMC start
Object [] adviceInvocationArgs = new Object [ this . parameterTypes . length ] ;
int numBound = 0 ;
// 如果存在参数JointPoint,则在这里放入参数。
if ( this . joinPointArgumentIndex != - 1 ) {
adviceInvocationArgs [ this . joinPointArgumentIndex ] = jp ;
numBound ++ ;
} // 如果存在参数JoinPoint.StaticPart,则在这里绑定
else if ( this . joinPointStaticPartArgumentIndex != - 1 ) {
adviceInvocationArgs [ this . joinPointStaticPartArgumentIndex ] = jp . getStaticPart ();
numBound ++ ;
}
// 省略...
return adviceInvocationArgs ;
}
}
adviceInvocationArgs[this.joinPointArgumentIndex] = jp
设置了 JoinPoint 参数。
看一下calculateArgumentBindings() 都做了什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
public final synchronized void calculateArgumentBindings () {
if ( this . argumentsIntrospected || this . parameterTypes . length == 0 ) {
return ;
}
int numUnboundArgs = this . parameterTypes . length ;
// 获取切面通知方法的参数列表
Class <?>[] parameterTypes = this . aspectJAdviceMethod . getParameterTypes ();
// 从这里可以看出,方法的第一个参数必须是JoinPoint类型的
if ( maybeBindJoinPoint ( parameterTypes [ 0 ] ) || maybeBindProceedingJoinPoint ( parameterTypes [ 0 ] ) ||
maybeBindJoinPointStaticPart ( parameterTypes [ 0 ] )) {
numUnboundArgs -- ;
}
// 类似这种 @Around("pc1()") around(ProceedingJoinPoint joinPoint)
// 方法只有一个参数,numUnboundArgs是0,就不需要参数绑定了
if ( numUnboundArgs > 0 ) {
// 需要通过切入点匹配返回的名称绑定参数
// need to bind arguments by name as returned from the pointcut match
bindArgumentsByName ( numUnboundArgs );
}
this . argumentsIntrospected = true ;
}
}
获取通知方法参数的个数赋值给 numUnboundArgs,获取到通知方法的所有参数类型。
拿第一个参数,去判断是否符合条件,如果符合条件,将变量 numUnboundArgs 减一。
如果方法中参数有多个,需要调用 bindArgumentsByName()。
看一下 maybeBindJoinPoint(),另外两个方法与这个类似:
1
2
3
4
5
6
7
8
9
10
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
private boolean maybeBindJoinPoint ( Class <?> candidateParameterType ) {
if ( JoinPoint . class == candidateParameterType ) { // 如果候选的参数类型是JoinPoint类型,则返回true,标识绑定成功。
this . joinPointArgumentIndex = 0 ;
return true ;
} else {
return false ;
}
}
}
bindArgumentsByName会调用到bindExplicitArguments() :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
private void bindExplicitArguments ( int numArgumentsLeftToBind ) {
Assert . state ( this . argumentNames != null , "No argument names available" );
this . argumentBindings = new HashMap <> ();
// 获取参数的数量
int numExpectedArgumentNames = this . aspectJAdviceMethod . getParameterCount ();
// 省略...
// 参数索引偏移量,也就是前面已经绑定过的参数,需要跳过,这里记录的是跳过已绑定参数后的下一个索引值。
int argumentIndexOffset = this . parameterTypes . length - numArgumentsLeftToBind ;
for ( int i = argumentIndexOffset ; i < this . argumentNames . length ; i ++ ) {
this . argumentBindings . put ( this . argumentNames [ i ] , i );
} // 省略部分代码...
// 把参数对应的名称和对应的参数的类型设置到pointcut中
// configure the pointcut expression accordingly.
configurePointcutParameters ( this . argumentNames , argumentIndexOffset );
}
}
获取到参数索引偏移量,这个偏移量是指前面已绑定的 JoinPoint。
来到 configurePointcutParameters() :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
private void configurePointcutParameters ( String [] argumentNames , int argumentIndexOffset ) {
int numParametersToRemove = argumentIndexOffset ;
if ( this . returningName != null ) {
numParametersToRemove ++ ;
}
if ( this . throwingName != null ) {
numParametersToRemove ++ ;
}
// 这里装的是参数名称,排除了已绑定的JoinPoint
String [] pointcutParameterNames = new String [ argumentNames . length - numParametersToRemove ] ;
Class <?>[] pointcutParameterTypes = new Class <?>[ pointcutParameterNames . length ] ;
Class <?>[] methodParameterTypes = this . aspectJAdviceMethod . getParameterTypes ();
int index = 0 ;
for ( int i = 0 ; i < argumentNames . length ; i ++ ) {
if ( i < argumentIndexOffset ) {
continue ;
}
if ( argumentNames [ i ] . equals ( this . returningName ) ||
argumentNames [ i ] . equals ( this . throwingName )) {
continue ;
}
// 将参数名称,封装到pointcutParameterNames中,将参数类型封装到pointcutParameterTypes中。
pointcutParameterNames [ index ] = argumentNames [ i ] ;
pointcutParameterTypes [ index ] = methodParameterTypes [ i ] ;
index ++ ;
}
// 将未绑定的参数名称数组、参数类型数组,封装到成员变量 pointcut。
this . pointcut . setParameterNames ( pointcutParameterNames );
this . pointcut . setParameterTypes ( pointcutParameterTypes );
}
}
跳过已绑定的位置,从后面的索引开始,将通知方法中的参数名称按照下标索引封装到数组 pointcutParameterNames 中,将参数类型封装到 pointcutParameterTypes 中。
8 InvokeAdviceMethodWithGivenArgs() 通知方法调用
通过反射,根据绑定的参数,方法,切面类,进行通知方法调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class AbstractAspectJAdvice implements Advice , AspectJPrecedenceInformation , Serializable {
protected Object invokeAdviceMethodWithGivenArgs ( Object [] args ) throws Throwable {
Object [] actualArgs = args ;
if ( this . aspectJAdviceMethod . getParameterCount () == 0 ) {
actualArgs = null ;
}
// 省略try块...
ReflectionUtils . makeAccessible ( this . aspectJAdviceMethod );
// 根据绑定的参数,反射调用通知方法。
// TODO AopUtils.invokeJoinpointUsingReflection
return this . aspectJAdviceMethod . invoke ( this . aspectInstanceFactory . getAspectInstance (), actualArgs );
}
}
不同种类的 MethodInterceptor,有不同的实现,而最终调用时,都是通过反射进行方法调用的。