Spring AOP源码解析:三:代理对象的执行,各种Advice的执行流程
Spring AOP源码解析:三:代理对象的执行,各种Advice的执行流程
- @AfterThrowing ---- AspectJAfterThrowingAdvice
- @Before ---- AspectJMethodBeforeAdvice
- @After ---- AspectJAfterAdvice
- @AfterReturning ---- AspectJAfterReturningAdvice
- @Around ----AspectJAroundAdvice (特殊)
目录:SpringAOP原理解析:
名词介绍
Advice: 即用来增强被代理对象功能的一个方法。比如第一章我们讲的:
AspectJAroundAdvice对应@Around,
AspectJMethodBeforeAdvice对应@Before,
AspectJAfterAdvice 对应@After,
AspectJAfterReturningAdvice 对应@AfterReturning
AspectJAfterThrowingAdvice 对应@AfterThrowing
Joinpoint: 抽象一次方法调用,代码的执行位置,它实时记录了当前方法调用信息,如调用的参数,调用的对象。它与Pointcut的区别是,Pointcut是静态的,它描述了一个拦截器应该执行的位置,这里的位置指的是类或方法,而Joinpoint描述的是具体哪个对象,哪一次方法调用,调用参数是什么。
MethodInvocation: aop联盟中的一个接口,抽象了一个方法的调用,继承了Joinpoint。
通用执行流程
无论是Cglib代理还是Jdk动态代理,最终都是通过AdvisedSupport中的getInterceptorsAndDynamicInterceptionAdvice方法,将Advisor列表封装成MethodIntercept链,然后再封装到ReflectiveMethodInvocation对象中,该对象实现了MethodInvocation接口,代表着此次方法调用的一个抽象。执行该对象的proceed()方法,就开始了代理对象的方法调用。
获取拦截器链
/** * AdvisedSupport类,这里使用了Pointcut的MethodMatcher来匹配方法,将这次方法匹配到的拦截器Advisor列表封装成Intercept列表,拦截此次调用 */ public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class<?> targetClass) { // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisor[] advisors = config.getAdvisors(); List<Object> interceptorList = new ArrayList<>(advisors.length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); Boolean hasIntroductions = null; for (Advisor advisor : advisors) { if (advisor instanceof PointcutAdvisor) { // Add it conditionally. // 这里advisor的一个实现类是InstantiationModelAwarePointcutAdvisorImpl PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; // 这里获取了Pointcut,一个实现类是AspectJExpressionPointcut if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); boolean match; if (mm instanceof IntroductionAwareMethodMatcher) { if (hasIntroductions == null) { hasIntroductions = hasMatchingIntroductions(advisors, actualClass); } match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); } else { match = mm.matches(method, actualClass); } if (match) { // 如果匹配成功 MethodInterceptor[] interceptors = registry.getInterceptors(advisor); if (mm.isRuntime()) { // Creating a new object instance in the getInterceptors() method // isn't a problem as we normally cache created chains. for (MethodInterceptor interceptor : interceptors) { interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); } } else { interceptorList.addAll(Arrays.asList(interceptors)); } } } } else if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; }
用拦截器链封装方法调用
在我们从代理对象中获取了所有的拦截器后,然后又将这些拦截器封装成了一个ReflectiveMethodInvocation,即将此次方法调用抽象成了一个MethodInvocation,在这个MethodInvocation内,采用责任链模式对这次方法调用进行处理。责任链模式:从第一个拦截器开始,链中收到的请求要么亲自处理它,要嘛转发给链中的下一个拦截器。
下面看到ReflectiveMethodInvocation这个类的proceed方法,它是整个调用链的入口,当没有其他拦截器时,会调用真正的方法,否则会从拦截器链中取出一个拦截器执行。
@Override @Nullable public Object proceed() throws Throwable { // We start with an index of -1 and increment early. // 没有其他拦截器了,调用目标方法。 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // 获取下一个拦截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // 如果是InterceptorAndDynamicMethodMatcher类型的,则需要执行一次匹配操作,否则直接执行拦截器方法,这里还不是特别清楚为什么要有这个区别 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); } }
每种Advisor的执行流程
每种Advice都继承于AbstractAspectJAdvice,我们上面列出的几种像AspectJAfterThrowingAdvice的Advice,它们都只是定义了何时执行下一个目标方法或拦截器方法以及何时执行Advice切面方法,真正执行Advice切面方法的实现逻辑在 invokeAdviceMethod方法中,由抽象类AbstractAspectJAdvice实现,它定义了如何调用Advice的方法。下一小节介绍invokeAdviceMethod的实现。
注:Advice方法指的是在@Aspect注解的切面对象中,由@AfterThrowing或者@Before等注解标注的方法,这些方法会被封装成Advice对象。
@AfterThrowing ---- AspectJAfterThrowingAdvice
AspectJAfterThrowingAdvice实现了MethodInterceptor,直接调用invoke方法即可。@AfterThrowing的实现比较简单,就是直接向后执行下一个拦截器,当抛出异常时调用Advice对应的方法。
public Object invoke(MethodInvocation mi) throws Throwable { try { // 执行ReflectiveMethodInvocation的proceed方法,这将会执行下一个拦截器或者执行目标方法 return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { // 调用切面方法 invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
@Before ---- AspectJMethodBeforeAdvice
AspectJMethodBeforeAdvice类没有实现MethodInterceptor接口,它是由MethodBeforeAdviceInterceptor包装了一层
/** * MethodBeforeAdviceInterceptor类,调用了AspectJMethodBeforeAdvice * 的before方法 */ @Override public Object invoke(MethodInvocation mi) throws Throwable { // 先执行Advice方法 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // 再执行下一个拦截器或者目标方法 return mi.proceed(); }
/** * AspectJMethodBeforeAdvice类的before方法 */ @Override public void before(Method method, Object[] args, @Nullable Object target) throws Throwable { invokeAdviceMethod(getJoinPointMatch(), null, null); }
@After ---- AspectJAfterAdvice
可以看到,@After也比较简单,就是在finally中调用Advice方法
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null, null); } }
@AfterReturning ---- AspectJAfterReturningAdvice
@AfterReturning和@Before有一点类似,AspectJAfterReturningAdvice没有实现MethodInterceptor接口,而是交给AfterReturningAdviceInterceptor来实现。AspectJAfterReturningAdvice不是直接执行Advice方法,而是要对方法的返回类型进行判断,然后再执行Advice方法。
从这里代码可以看出@AfterReturning和@After的区别,@AfterReturning是在后面串行的,只有当前面的执行没有抛出异常,且返回类型匹配的情况下才会执行,而@After是在finally代码块执行的,尽管目标方法抛出异常也会执行。
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(); //执行Advice方法,当mi.proceed()没有抛出异常时,才能走到Advice方法 this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; } }
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 { //判断是否应该调用切面Advice方法 if (shouldInvokeOnReturnValueOf(method, returnValue)) { invokeAdviceMethod(getJoinPointMatch(), returnValue, null); } } private boolean shouldInvokeOnReturnValueOf(Method method, @Nullable Object returnValue) { // 获取指定的返回类型,默认是Object类,可以通过@AfterReturning注解中的returning参数来定制化这个类 Class<?> type = getDiscoveredReturningType(); // 获取返回值的泛型 Type genericType = getDiscoveredReturningGenericType(); // If we aren't dealing with a raw type, check if generic parameters are assignable. // 执行类型匹配 return (matchesReturnValue(type, method, returnValue) && (genericType == null || genericType == type || TypeUtils.isAssignable(genericType, method.getGenericReturnType()))); } private boolean matchesReturnValue(Class<?> type, Method method, @Nullable Object returnValue) { if (returnValue != null) { return ClassUtils.isAssignableValue(type, returnValue); } else if (Object.class == type && void.class == method.getReturnType()) { return true; } else { return ClassUtils.isAssignable(type, method.getReturnType()); } } }
这里简单介绍下如何定制getDiscoveredReturningType();的返回类型的问题,该返回值默认为Object类。我们可以通过@AfterReturning注解的returning参数,来定制化。returning指定了Advice方法的形参名,如果形参名和returning参数值一致的话,那么这个形参的类型就是getDiscoveredReturningType将返回的类型。具体源码这里不展开,参考ReflectiveAspectJAdvisorFactory的getAdvice方法。 如下面代码,参数haha是形参名,类型是String,那么getDiscoveredReturningType方法将返回java.lang.String。
@AfterReturning(value = "annotationPointcut()", returning = "haha") public void doAfterReturning(String haha) { // start stopwatch System.out.println("doAfterReturning"); }
@Around ----AspectJAroundAdvice (特殊)
特殊性: 在AspectJAroundAdvice类中的invoke方法中,不同于前面讲到的Advice, 前面的所有Advice都包括了mi.proceed()和invokeAdviceMethod()两部分,而AspectJAroundAdvice类中只有invokeAdviceMethod()这一部分,它将MethodInvocation封装到了ProceedingJoinPoint中,通过这种方法,它责任链的控制权交给了对应的Advice方法,我们知道只有@Around注释的Advice方法才能使用ProceedingJoinPoint类型的参数,而其他的Advice方法只能使用JoinPoint类型的参数。
责任链的控制权指的是,我们可以在@Around注解对应的Advice方法中控制是否继续向下执行,即mi.procceed()是否执行可以由我们来编写,而其他的Advice方法不能这样。 例如:
@Around(value = "annotationPointcut()") public void doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("doAround"); //假如这里我们不执行pjp.proceed()方法,那么该目标方法就不会调用,后面责任链上如果还有其他拦截器,那么这些拦截器将不会被执行,我们可以控制何时继续向下执行 if (pjp.getArgs().length != 0) { pjp.proceed(); } else{ // 目标方法不会被执行,被完全拦截掉 } } @Before(value = "annotationPointcut()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 对于@Before,我们只能 joinPoint.getArgs(); System.out.println("Before"); }
那么我们来看一下AspectJAroundAdvice的invoke代码,很简单,它的区别就是调用invokeAdviceMethod时,传入的不是JoinPoint类型,而是ProceedingJoinPoint类型。
@Override public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); }
调用Advice方法:invokeAdviceMethod
众所周知,Advice方法的参数和返回值可以有多种情况,比如@Around对应的Advice方法可以没有参数,也可以有一个JoinPoint参数,@AfterReturing可以通过注解returning属性获取到目标方法调用的返回值作为参数,@AfterThrowing可以通过throwing参数属性来获取目标参数抛出的异常。所以invokeAdviceMethod需要获取调用Advice方法所真正需要的参数。
invokeAdviceMethod方法主要做了两步:
- 获取调用Advice方法需要的参数
- 利用反射调用Advice方法
protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); }
获取调用Advice方法需要的参数
这里argBinding方法就是获取调用Advice方法所需要的参数:
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; //当Advice方法有Joinpoint参数时,设置该参数 if (this.joinPointArgumentIndex != -1) { adviceInvocationArgs[this.joinPointArgumentIndex] = jp; numBound++; } else if (this.joinPointStaticPartArgumentIndex != -1) { adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart(); numBound++; } if (!CollectionUtils.isEmpty(this.argumentBindings)) { // binding from pointcut match if (jpMatch != null) { PointcutParameter[] parameterBindings = jpMatch.getParameterBindings(); for (PointcutParameter parameter : parameterBindings) { String name = parameter.getName(); Integer index = this.argumentBindings.get(name); adviceInvocationArgs[index] = parameter.getBinding(); numBound++; } } // 如果是@AfterReturning,对应上面讲到的returning属性的形参名称 if (this.returningName != null) { Integer index = this.argumentBindings.get(this.returningName); adviceInvocationArgs[index] = returnValue; numBound++; } // 如果是@AfterThrowing,那么可以将抛出的异常设置到参数中 if (this.throwingName != null) { Integer index = this.argumentBindings.get(this.throwingName); adviceInvocationArgs[index] = ex; numBound++; } } if (numBound != this.parameterTypes.length) { throw new IllegalStateException("Required to bind " + this.parameterTypes.length + " arguments, but only bound " + numBound + " (JoinPointMatch " + (jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)"); } return adviceInvocationArgs; }
调用目标Advice方法
调用目标方法主要分2部分:this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
- 从IOC容器中获取该Advice方法对应的@Aspect注解的对象bean。
- 通过反射机制调用该bean的Advice方法。
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 // 获取到该@Aspect注解对应的bean,调用该bean对应的Advice方法 // this.aspectInstanceFactory是对容器的一个包装,通过懒加载的方式获取对应的bean return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }
总结
一图流:this代表ReflectiveMethodInvocation对象。希望看完前面的内容后,能看懂下面这张图,如果有不懂的请留言,谢谢。
名词总结:
Spring相关接口:
@Aspect 切面, 一个Bean,Spring会扫描所有带@Aspect注解的类中的方法,并将带有@Before,@Around等注解的方法,实例化成对应的Advice对象。
Advice: action to take at a joinpoint。记录了要AOP拦截器要执行的内容,对应着Aspect中的一个方法。
Pointcut: 用于判断某一个Advice是否可以在某个位置执行,包含两个属性:ClassFilter,MethodMatcher,用于记录某一个Advice是否能拦截某个类的某个方法。
Advisor: 包含了Advice和Pointcut,一个Advisor对应一个拦截器,代理对象中记录了一个Advisor的列表,用于拦截目标对象的方法调用。
Advised:记录了一个代理工厂用来创建一个代理对象的所有配置信息,核心内容包括了Advisor列表
aop联盟接口:
Advice:拦截器方法
Joinpoint:抽象一次方法调用,代码的执行位置,它实时记录了当前方法调用信息,如调用的参数,调用的对象。它与Pointcut的区别是,Pointcut是静态的,它描述了一个拦截器应该执行的位置,这里的位置指的是类或方法,而Joinpoint描述的是具体哪个对象,哪一次方法调用,调用参数是什么。
MethodInterceptor: Spring中会将Advice封装成MethodInterceptor,采用责任链的方式进行调用,继承了Joinpoint。
- Spring3.1.0实现原理分析(十).AOP代理对象执行拦截过程
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
- Spring源码分析----建立AopProxy代理对象和AOP拦截器的调用
- Spring AOP 源码分析 - 创建代理对象
- Spring基于注解形式的 AOP的原理流程及源码解析(一)
- Spring进阶之路(10)-Advice简介以及通过cglib生成AOP代理对象
- Spring源码之 AOP 代理流程
- Spring基于注解形式的 AOP的原理流程及源码解析(二)
- Spring之注解版AOP的原理和源码执行流程
- Spring基于注解形式的 AOP的原理流程及源码解析(三)
- 深入理解Spring 之 源码剖析 SpringBoot Aop 切面编织过程和代理执行过程
- Spring基于注解形式的 AOP的原理流程及源码解析(四)
- Spring AOP源码解析——AOP动态代理原理和实现方式
- Spring源码阅读4.2-Aspecjt AOP之代理对象的创建
- spring 代理对象方法增强源码解析
- Spring3.1.0实现原理分析(十).AOP之代理对象执行拦截过程
- spring 代理对象方法增强源码解析222222
- spring 源码探索 -- aop 标签解析和创建代理
- Spring学习第二天Aop_invoke的代理对象生成解析
- SpringAOP源码解析之代理创建篇