源碼分析基於spring 4.3.x
本文承接上一篇文章對Spring AOP的分析,繼續分析spring如何對AOP中多個通知進行鏈式調用的。
關於閱讀源碼的思路,可參考 -- 如何閱讀java源碼java
《Spring源碼解析 -- AOP原理(1)》 中說了,JdkDynamicAopProxy實現了InvocationHandler,正是這個類調用AOP通知中定義的加強方法。
JdkDynamicAopProxy#invokespring
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { ... // #1 Object retVal; if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // #2 if (chain.isEmpty()) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); // #3 } else { invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // #4 retVal = invocation.proceed(); // #5 } Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // #6 retVal = proxy; } 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()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } }
#1
處理equals/hashCode等方法,直接調用目標方法#2
getInterceptorsAndDynamicInterceptionAdvice -- 獲取方法攔截器#3
沒有方法攔截器,直接調用目標方法#4
構造調用鏈ReflectiveMethodInvocation#5
調用調用鏈#6
處理特殊場景 -- 方法返回值爲this微信
AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice -> DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice源碼分析
public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, Class<?> targetClass) { // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); for (Advisor advisor : config.getAdvisors()) { if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { // #1 MethodInterceptor[] interceptors = registry.getInterceptors(advisor); // #2 MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { // #3 if (mm.isRuntime()) { // #4 // 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)); // #5 } } else { interceptorList.addAll(Arrays.asList(interceptors)); // #6 } } } } 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; }
#1
判斷class是否匹配pointcut#2
getInterceptors -- 將advisor轉化爲MethodInterceptor方法攔截器,MethodInterceptor#invoke負責調用被攔截的方法#3
判斷method是否匹配pointcut#4
是否運行時判斷#5
添加InterceptorAndDynamicMethodMatcher到結果#6
添加MethodInterceptor到結果ui
DefaultAdvisorAdapterRegistry#getInterceptors:this
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3); Advice advice = advisor.getAdvice(); if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor) advice); } for (AdvisorAdapter adapter : this.adapters) { if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } return interceptors.toArray(new MethodInterceptor[interceptors.size()]); }
有三個Advice沒有實現MethodInterceptor,經過DefaultAdvisorAdapterRegistry#adapters適配lua
public DefaultAdvisorAdapterRegistry() { registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); registerAdvisorAdapter(new AfterReturningAdviceAdapter()); registerAdvisorAdapter(new ThrowsAdviceAdapter()); }
回到JdkDynamicAopProxy#invoke方法#5
步驟,調用調用鏈
ReflectiveMethodInvocation#proceedspa
public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { // #1 return invokeJoinpoint(); // #2 } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //#3 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { //#4 ... } 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); //#5 } }
#1
判斷是否到攔截器鏈末尾,注意currentInterceptorIndex初始值是-1#2
攔截器已經調用完,調用被攔截的方法#3
獲取下一個攔截器#4
是否爲須要運行時判斷#5
直接調用攔截器code
調用鏈ReflectiveMethodInvocation中的節點是攔截器MethodInterceptor。
ReflectiveMethodInvocation#proceed方法會獲取下一個攔截器,並調用MethodInterceptor#invoke方法。
注意,調用攔截器MethodInterceptor#invoke方法時會將調用鏈ReflectiveMethodInvocation做爲參數,攔截器MethodInterceptor#invoke執行完邏輯後,又調用ReflectiveMethodInvocation#proceed,繼續調用下一個攔截器。orm
看看AspectJAroundAdvice的實現
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; // #1 ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); // #2 JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); // #3 }
#1
獲取調用鏈#2
使用調用鏈構造一個MethodInvocationProceedingJoinPoint#3
調用加強方法
protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); // #1 }
#1
invokeAdviceMethodWithGivenArgs -- 經過反射調用加強方法,就是執行Around通知
argBinding -- 負責參數綁定
AbstractAspectJAdvice#argBinding
protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) { calculateArgumentBindings(); //#1 // AMC start Object[] adviceInvocationArgs = new Object[this.parameterTypes.length]; int numBound = 0; if (this.joinPointArgumentIndex != -1) { //#2 adviceInvocationArgs[this.joinPointArgumentIndex] = jp; numBound++; } else if (this.joinPointStaticPartArgumentIndex != -1) { adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart(); numBound++; } if (!CollectionUtils.isEmpty(this.argumentBindings)) { //#3 // 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++; } } // binding from returning clause if (this.returningName != null) { Integer index = this.argumentBindings.get(this.returningName); adviceInvocationArgs[index] = returnValue; numBound++; } // binding from thrown exception 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; }
#1
經過反射獲取參數名,返回名等信息,設置joinPointArgumentIndex,argumentBindings等屬性#2
添加JoinPointMatch到加強方法的參數中#3
添加被攔截方法參數,返回值,異常等數據到加強方法的參數中
在環繞通知中,咱們須要編寫以下代碼
@Around("...") public Object methodAroundLog(ProceedingJoinPoint joinPoint) throws Throwable { Object result = joinPoint.proceed(joinPoint.getArgs()); return result; }
這裏joinPoint.proceed(joinPoint.getArgs());
調用了下一個攔截器(MethodInvocationProceedingJoinPoint#proceed會調用ReflectiveMethodInvocation#proceed),這樣才能繼續調用攔截器調用鏈。
@Before通知會調用MethodBeforeAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); // #1 return mi.proceed(); // #2 }
#1
執行@Before加強#2
繼續攔截器調用鏈
@After通知會調用AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); // #1 } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { invokeAdviceMethod(getJoinPointMatch(), null, ex); // #2 } throw ex; } }
#1
先調用攔截器鏈#2
執行@After加強
到這裏,spring aop的源碼解析就完成了。
這部分仍是挺複雜的,須要耐心點看源碼。
若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!