在之前的文章我们分析了通知器的创建与筛选和AOP创建代理对象的过程,现在代理对象已经有了,接下来我们看一下是如何执行通知器的逻辑的。
通过阅读这篇文章,可以了解到以下几个问题:
下面我们可以带着这些疑问来看
本文依据 JdkDynamicAopProxy
来分析,对CGLIB感兴趣的同学看一看 ObjenesisCglibAopProxy
相关代码。 JdkDynamicAopProxy实现了 InvocationHandler
接口,我们来看下 invoke()
方法:
//JdkDynamicAopProxy.java public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { //如果目标对象没有定义equals()方法的话,就会直接调用而不会增强 <1> if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } //如果目标对象没有定义hashCode()方法的话,就会直接调用而不会增强 else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知 else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; // 如果 exposeProxy 属性为 true,则暴露代理对象 // exposeProxy 是 @EnableAspectJAutoProxy 注解的属性之一 <2> if (this.advised.exposeProxy) { // Make invocation available if necessary. // 向 AopContext 中设置代理对象 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } //获得目标对象的类 <3> target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); //获取可以应用到此方法上的 Interceptor 拦截器 列表,并且排序 <4> List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args) <5> if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } <6> else { //创建 MethodInvocation,将拦截器链放入 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 执行拦截器链 retVal = invocation.proceed(); } // 获取方法返回值类型 <7> Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // 如果方法返回值为 this,即 return this; 则将代理对象 proxy 赋值给 retVal retVal = proxy; } // 如果返回值类型为基础类型,比如 int,long 等,当返回值为 null,抛出异常 else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } 复制代码
这个invoke()方法主要有以下几个步骤:
equals()、hashCode() exposeProxy
我们重点关注第<4>步和第<6>步,这两个地方非常重要,第<2>步涉及比较多,最后我们再分析,先来看下第<4>步。
代码:
//AdvisedSupport.java public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); //从缓存中获取 List<Object> cached = this.methodCache.get(cacheKey); if (cached == null) { //获取所有的拦截器 cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); this.methodCache.put(cacheKey, cached); } return cached; } 复制代码
继续深入:
//DefaultAdvisorChainFactory.java /** * 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor, * 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断 * 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回. */ @Override public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class<?> targetClass) { List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); //查看是否包含IntroductionAdvisor boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); //这里实际上注册一系列AdvisorAdapter,用于将 通知Advisor 转化成 方法拦截器MethodInterceptor AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); //遍历 for (Advisor advisor : config.getAdvisors()) { if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; //通过ClassFilter对当前Bean类型进行匹配 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { //将 通知Advisor 转化成 拦截器Interceptor MethodInterceptor[] interceptors = registry.getInterceptors(advisor); MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); //检查当前advisor的pointcut是否可以匹配当前方法 if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { 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; // IntroductionAdvisor 类型的通知器,仅进行类级别的匹配即可 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; } 复制代码
这里的主逻辑不是很复杂:
这里返回的拦截器链是 有序的 ,按照afterReturn、after、around、before排好序,(具体排序是在获取所有通知的时候 sortAdvisors(eligibleAdvisors)
),方便后面执行。
现在有了拦截器链,接下来再看下怎么执行的:
//ReflectiveMethodInvocation.java //当前拦截器下标 private int currentInterceptorIndex = -1; //拦截器链集合 protected final List<?> interceptorsAndDynamicMethodMatchers; public Object proceed() throws Throwable { // We start with an index of -1 and increment early. // 拦截器链中的最后一个拦截器执行完 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //执行目标方法 return invokeJoinpoint(); } //每次执行新的拦截器,下标+1 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; // 如果是动态切点,需要进行参数匹配,以确定是否需要执行该动态横切逻辑 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { //动态切点匹配失败,略过当前Intercetpor,调用下一个Interceptor return proceed(); } } else { //执行当前Intercetpor return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } } 复制代码
proceed 根据 currentInterceptorIndex
按照倒序执行拦截器链,每次执行完+1,当所有拦截器执行完后,再执行目标方法。 这里我们可以提出几个疑问:
我们带着这2个疑问往下看,
proceed中的 invoke(this)
方法根据通知的类型,有不同的实现类,如:
@Before对应 MethodBeforeAdviceInterceptor
@After对应 AspectJAfterAdvice
@AfterReturning对应 AfterReturningAdviceInterceptor
@AfterThrowing对应 AspectJAfterThrowingAdvice
@Around对应 AspectJAroundAdvice
由于拦截器链是按照 倒序 排列的,我们先来看下 后置通知 的代码:
//AspectJAfterAdvice.java public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterAdvice( Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif); } @Override public Object invoke(MethodInvocation mi) throws Throwable { try { //执行下一个拦截器链 return mi.proceed(); } finally { //执行后置逻辑 invokeAdviceMethod(getJoinPointMatch(), null, null); } } @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return true; } } 复制代码
可以看到,后置拦截器是会先继续执行下一个拦截器,当拦截器链执行完毕之后, proceed()
再执行目标方法,最后执行后置逻辑。
我们再来看下 环绕拦截器 :
//AspectJAroundAdvice.java 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); } 复制代码
环绕拦截器会直接执行环绕逻辑,而 由于我们之前在 LogAspect
中配置了如下代码:
@Aspect @Component @EnableAspectJAutoProxy public class LogAspect { @Pointcut("execution(* com.mydemo.work.StudentController.getName(..))") private void log(){} @Before("log()") public void doBefore() { System.out.println("===before"); } @After("execution(* com.mydemo.work.StudentController.getName(..))") public void doAfter() { System.out.println("===after"); } @AfterReturning("execution(* com.mydemo.work.StudentController.getName(..))") public void doAfterReturn() { System.out.println("===afterReturn"); } @Around("execution(* com.mydemo.work.StudentController.getName(..))") public void doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("===around before"); pjp.proceed(); System.out.println("===around after"); } } 复制代码
所以该拦截器会先执行 ========around before
,然后再执行下一个拦截器。
我们再来看下 前置拦截器 :
//MethodBeforeAdviceInterceptor.java public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { private MethodBeforeAdvice advice; /** * Create a new MethodBeforeAdviceInterceptor for the given advice. * @param advice the MethodBeforeAdvice to wrap */ public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { //执行前置逻辑 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); //执行下一个拦截器 return mi.proceed(); } } 复制代码
可以看到前置拦截器在执行完 前置方法 之后,又调用 MethodInvocation#proceed()
方法,继续去执行下一个拦截器。
到这里大家可能有点绕,我们来捋一下通知的执行顺序,也就是拦截器的执行顺序。
首先,前面说过拦截器链是按照倒序排序的,如下:
可以看到这里的通知是按照:afterReturn、after、around、before排序的。
画一下执行流程图:
再用单元测试跑一遍LogAspect
的相关代码,打印结果如下:
===around before ===before do getName ===around after ===after ===afterReturn 复制代码
和我们猜测的一样。现在我们可以回答前面的问题了,就是一个方法匹配多个通知时,按照什么顺序执行。
但是,在实际中,通常一个方法可能被多个Aspect拦截,比如我们想让业务方法先被 日志Aspect 拦截,然后被 异常Aspect拦截。
Spring框架已经替我们想到了这个问题,如果我们有多个Aspect,实际上它们会随机执行,没有明确的顺序。但是Spring提供了 @Order
注解,可以让我们指定Aspect的执行顺序。 比如我们新加一个处理异常的Aspect:
@Aspect @Component @EnableAspectJAutoProxy @Order(2) public class ErrorAspect { @Pointcut("execution(* com.mydemo.work.StudentController.getName(..))") private void log(){} @Before("log()") public void doBeforeError() { System.out.println("=== error before"); } @After("execution(* com.mydemo.work.StudentController.getName(..))") public void doAfterError() { System.out.println("=== error after"); } @AfterReturning("execution(* com.mydemo.work.StudentController.getName(..))") public void doAfterReturnError() { System.out.println("=== error afterReturn"); } @Around("execution(* com.mydemo.work.StudentController.getName(..))") public void doAroundError(ProceedingJoinPoint pjp) throws Throwable { System.out.println("=== error around before"); pjp.proceed(); System.out.println("=== error around after"); } } 复制代码
同时给 LogAspect
加上注解 @Order(1)
,也就意味着 LogAspect
的通知要比 ErrorAspect
先执行。 先来单元测试跑一下看看结果:
===around before ===before === error around before === error before do getName === error around after === error after === error afterReturn ===around after ===after ===afterReturn 复制代码
可以看到,确实是 LogAspect
的通知先执行了,这是为什么呢?我们来debug源码看一下,主要看拦截器链,如图:
可以看到,拦截器链已经按照顺序排列好了,所以代码只要按照这个拦截器链顺序执行,就能保证2个切面有序拦截。
那么它为什么要这样排序呢?我们来画个图:
如上图所示,这2个Aspect就像2个圆圈在外面拦截,中间是目标方法。
当一个请求进来要执行目标方法:
@Order(1) @Order(2) @Order(2) @Order(1)
到这里我们多个通知的执行顺序就分析完了,这里面还是要去从源码层面理解,不能死记硬背,这样才能记得牢固。
上面我们分析 invoke()
方法的时候,有下面这段代码:
// 如果 exposeProxy 属性为 true,则暴露代理对象 // exposeProxy 是 @EnableAspectJAutoProxy 注解的属性之一 if (this.advised.exposeProxy) { // Make invocation available if necessary. // 向 AopContext 中设置代理对象 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } 复制代码
本小节我们就来详细分析一下这里是什么意思。
首先 exposeProxy 是注解 @EnableAspectJAutoProxy
中的一个属性,它可以被设置为true或false,默认是false。
这个属性是为了 解决目标方法调用同对象中其他方法时,其他方法的切面逻辑无法执行的问题 。
啥意思呢?我们来举个例子:
public class Student implements Person { @Override public void haveFun() { System.out.println("篮球"); this.hello("足球"); } @Override public void haveFun(String action) { System.out.println("haveFun " + action); } } 复制代码
在同一个类 Student.class
中,一个 haveFun()
方法调用了本类中的另一个 haveFun(String action)
方法,此时 haveFun(String action)
上的切面逻辑就无法执行了。
那为什么会这样呢?
我们知道AOP的本质就是给目标对象生成一个代理对象,对原本的方法进行增强,之后执行的方法都是我们代理类中的方法。
而 haveFun()
方法中使用的是 this.hello("足球")
,这里的this不是代理对象,而是原始对象,因此通过原始对象调用 haveFun(String action)
是不会被增强的,所以切面就不会起作用。
那么问题又来了,为什么这里的this不是代理对象,而是原始对象呢?
上面代码中有一个 ReflectiveMethodInvocation#proceed()
方法如下:
public Object proceed() throws Throwable { // We start with an index of -1 and increment early. // 拦截器链中的最后一个拦截器执行完 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //执行目标方法 return invokeJoinpoint(); } //...省略 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } //...省略 } else { //执行当前Intercetpor return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } } 复制代码
在所有拦截器执行完之后,会去执行目标方法,我们跟踪这个方法 invokeJoinpoint()
//ReflectiveMethodInvocation.java protected Object invokeJoinpoint() throws Throwable { //重点 this.target return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments); } 复制代码
//ReflectiveMethodInvocation.java public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args) throws Throwable { // Use reflection to invoke the method. try { ReflectionUtils.makeAccessible(method); //重点 target return method.invoke(target, args); } //...省略 } 复制代码
可以看到,这里调用的是原始目标对象 target
来执行我们的目标方法,所以此时 haveFun()
方法中的 this.hello("足球")
的这个this其实是原始目标对象。
所以 exposeProxy
这个属性就是来解决这个问题的,那么它是如何起作用的呢? 我们回过头来看代码:
// 如果 exposeProxy 属性为 true,则暴露代理对象 // exposeProxy 是 @EnableAspectJAutoProxy 注解的属性之一 if (this.advised.exposeProxy) { // Make invocation available if necessary. // 向 AopContext 中设置代理对象 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } 复制代码
继续跟踪:
//AopContext.java //存储代理对象 ThreadLocal private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy"); static Object setCurrentProxy(@Nullable Object proxy) { Object old = currentProxy.get(); if (proxy != null) { //存储代理对象到 ThreadLocal currentProxy.set(proxy); } else { currentProxy.remove(); } return old; } 复制代码
如果 exposeProxy
被设置为了true,会把代理对象存到 ThreadLocal
中,而在本类中调用的时候,会从 ThreadLocal
中获取代理类来调用目标方法,就可以解决本来调用的问题。
最后再来看下面这种情况:
public class Student implements Person { @Override @Transactional public void haveFun() { System.out.println("篮球"); this.hello("足球"); } @Override @Transactional public void haveFun(String action) { System.out.println("haveFun " + action); } } 复制代码
这种情况下, haveFun(String action)
的事务不会生效,原因就是我们刚才分析的。实际上 @Transactional
本质上也是AOP实现的,所以也会有这个问题。
解决方案就是给 @EnableAspectJAutoProxy
注解设置 exposeProxy=true
总结
时序图:
到这里SpringAOP的源码分析就告一段落了,由于本人经验和技术水平有限,所以只能先了解这么多,如有错误,欢迎提出意见。
参考:
www.tianxiaobo.com/2018/06/22/…