The two powerful features of Spring are IOC and AOP. We know that the process of implementing AOP in Spring is processed in the Spring Bean post processor and executed during Bean initialization. Next, let's take a detailed look at how the source code does
All methods that generate proxy objects will be delegated to invocationhandler For proxy execution, let's first look at the implementation of the invoke method in JdkDynamicAopProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; Class var8; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { Boolean var18 = this.equals(args[0]); return var18; } if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { Integer var17 = this.hashCode(); return var17; } if (method.getDeclaringClass() != DecoratingProxy.class) { Object retVal; if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); return retVal; } if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } target = targetSource.getTarget(); Class<?> targetClass = target != null ? target.getClass() : null; List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { 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); } Object var12 = retVal; return var12; } var8 = AopProxyUtils.ultimateTargetClass(this.advised); } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } return var8; }
Let's just look at a few important methods
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Spring uses the responsibility chain pattern to implement AOP, and the function of this method is to obtain the chain
We can understand that this chain is used to obtain the collection of methods used to enhance the target object (according to the execution order). If the chain we get is empty, it will directly return to the original section and return to the original object; if there is, it will trigger the execution of the interceptor chain (the chain).
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed();
Let's look at the processed method again
@Nullable public Object proceed() throws Throwable { if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return this.invokeJoinpoint(); } else { Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice; Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass(); return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed(); } else { return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this); } } }
We all know that there are actually five links of enhancement methods on the target method
They are: Post notification, return notification, exception notification, surround notification and pre notification
The order in which they trigger execution, that is, the order in which they are added, is
1,around
2,before
3,after
4,afterReturning
5,afterThrowing
Our processed method will first judge whether we have configured the methods of the corresponding stage in order. If so, execute the current enhanced method. If not, judge the next method again by recursive call; At the same time, a count will be maintained on the chain to manage the judged stage.
for instance
After we get the interceptor chain, we will first enter the around stage for judgment
1. We will first judge whether we have configured corresponding enhancement methods in the around stage. At this time, the chain count is increased by one, indicating that we are about to enter the next stage.
2. If there is an enhanced implementation of the method at around, the enhanced method is executed.
3. If not, recursively call the proceed method to proceed to the next stage of judgment
It should be noted here that we use recursive call to enter the method judgment, that is, after each stage of judgment, we will return to the interceptor chain, and the interceptor will tell us that we should enter that stage for judgment next time.
The purpose of returning to the interceptor chain here is because we do not necessarily configure the enhancement methods according to the established order in the configuration process. If the judgment is not specified by the interceptor chain, the phase sequence will be disordered.