Related reading
- Pointcut, the basic component of Spring AOP
- Spring AOP basic component Advisor
- Spring AOP basic component Advised
brief introduction
Represents the tag interface of notification. There are many implementation classes of notification, such as Interceptor;
Source code
public interface Advice { }
Implementation subclass
BeforeAdvice
brief introduction
Tag interface of pre notification; Implementation subclasses include: MethodBeforeAdvice;
Spring currently only supports method pre notification;
AfterAdvice
brief introduction
The tag interface of post notification and its implementation subclasses include: AfterReturningAdvice and ThrowsAdvice;
AbstractAspectJAdvice
brief introduction
The basic class of AOP Advice that wraps the notification method marked by AspectJ aspect or AspectJ annotation;
Core code
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable { // Call enhancement method protected Object invokeAdviceMethod( @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); } // Call enhancement method protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); } // Call the enhancement method according to the given parameters protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterCount() == 0) { actualArgs = null; } try { // Reflection call enhancement method ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); 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(); } } }
AspectJMethodBeforeAdvice
brief introduction
Spring AOP Advice packaging AspectJ pre notification method;
Core code
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); } @Override public boolean isBeforeAdvice() { return true; } @Override public boolean isAfterAdvice() { return false; } }
AspectJAfterAdvice
brief introduction
Spring AOP Advice that wraps AspectJ's final notification method;
Core code
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable { @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { // The notification will be executed whether it returns normally or not invokeAdviceMethod(getJoinPointMatch(), null, null); } } @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return true; } }
AspectJAfterReturningAdvice
brief introduction
Spring AOP Advice packaging AspectJ post notification method;
Core code
public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice, Serializable { @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return true; } @Override public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable { // Judge whether to call according to the return value if (shouldInvokeOnReturnValueOf(method, returnValue)) { invokeAdviceMethod(getJoinPointMatch(), returnValue, null); } } private boolean shouldInvokeOnReturnValueOf(Method method, @Nullable Object returnValue) { Class<?> type = getDiscoveredReturningType(); Type genericType = getDiscoveredReturningGenericType(); // If we aren't dealing with a raw type, check if generic parameters are assignable. // Judge whether the return value type matches. If the return value type is a generic type, the generic parameters should also match 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) { // Whether the return value is the specified type return ClassUtils.isAssignableValue(type, returnValue); } else if (Object.class == type && void.class == method.getReturnType()) { // The method will match the Object type even if it has no return value return true; } else { // Method returns whether the value type matches the specified type return ClassUtils.isAssignable(type, method.getReturnType()); } } }
AspectJAfterThrowingAdvice
brief introduction
Spring AOP Advice that wraps the exception notification method thrown by AspectJ;
Core code
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable { @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return true; } @Override public void setThrowingName(String name) { setThrowingNameNoCheck(name); } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { // Judge whether to call according to the exception type if (shouldInvokeOnThrowing(ex)) { invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } } private boolean shouldInvokeOnThrowing(Throwable ex) { // Does the current exception match the specified exception type return getDiscoveredThrowingType().isAssignableFrom(ex.getClass()); } }
AspectJAroundAdvice
brief introduction
Spring AOP Advice that wraps the AspectJ surround notification method;
Core code
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable { @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return false; } @Override protected boolean supportsProceedingJoinPoint() { return true; } @Override @Nullable 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); } protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) { return new MethodInvocationProceedingJoinPoint(rmi); } }
MethodInterceptor
brief introduction
Intercept the call of the interface on the way to the target, and the subclass implements the additional processing logic before and after the interface call;
Core code
@FunctionalInterface public interface MethodInterceptor extends Interceptor { // By calling a method call, you can perform additional logic before and after the method call @Nullable Object invoke(@Nonnull MethodInvocation invocation) throws Throwable; }
ConstructorInterceptor
brief introduction
Intercept the construction method, and the subclass implements the additional processing logic before and after the construction method call;
Core method
public interface ConstructorInterceptor extends Interceptor { // Calling a constructor call allows additional logic to be executed before and after the constructor call @Nonnull Object construct(ConstructorInvocation invocation) throws Throwable; }
MethodBeforeAdviceInterceptor
brief introduction
Interceptor for packaging MethodBeforeAdvice;
Core code
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable { // Before advice private final MethodBeforeAdvice advice; public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { // Verification pre notification Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { // Execute pre notification logic first this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // Then execute the method call return mi.proceed(); } }
AfterReturningAdviceInterceptor
brief introduction
Package the Interceptor of AfterReturningAdvice;
Core code
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { // Post notification private final AfterReturningAdvice advice; public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { // Post verification notification Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { // Execute method call first Object retVal = mi.proceed(); // Then execute the post notification logic this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; } }
ThrowsAdviceInterceptor
brief introduction
Interceptor for packaging ThrowsAdvice;
The signature of the exception handling method in the ThrowsAdvice implementation subclass must be in the following format:
void afterThrowing([Method, args, target], ThrowableSubclass);
For example:
public void afterThrowing(Exception ex) public void afterThrowing(RemoteException) public void afterThrowing(Method method, Object[] args, Object target, Exception ex) public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
Core code
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice { private static final String AFTER_THROWING = "afterThrowing"; // Throw exception enhancement private final Object throwsAdvice; // Cache exception handling method, with exception type as KEY private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<>(); public ThrowsAdviceInterceptor(Object throwsAdvice) { // Verification throw exception enhancement Assert.notNull(throwsAdvice, "Advice must not be null"); this.throwsAdvice = throwsAdvice; Method[] methods = throwsAdvice.getClass().getMethods(); // Traverse all methods enhanced by throwing exceptions and find exception handling methods for (Method method : methods) { if (method.getName().equals(AFTER_THROWING) && (method.getParameterCount() == 1 || method.getParameterCount() == 4)) { Class<?> throwableParam = method.getParameterTypes()[method.getParameterCount() - 1]; if (Throwable.class.isAssignableFrom(throwableParam)) { // Cache exception handling methods, which is convenient to obtain exception handling methods directly according to exception types this.exceptionHandlerMap.put(throwableParam, method); if (logger.isDebugEnabled()) { logger.debug("Found exception handler method on throws advice: " + method); } } } } // Verify the effectiveness of exception handling methods if (this.exceptionHandlerMap.isEmpty()) { throw new IllegalArgumentException( "At least one handler method must be found in class [" + throwsAdvice.getClass() + "]"); } } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { try { // Execute method call return mi.proceed(); } catch (Throwable ex) { // If an exception occurs, obtain the corresponding exception handling method according to the exception type for processing Method handlerMethod = getExceptionHandler(ex); if (handlerMethod != null) { invokeHandlerMethod(mi, ex, handlerMethod); } throw ex; } } @Nullable private Method getExceptionHandler(Throwable exception) { Class<?> exceptionClass = exception.getClass(); if (logger.isTraceEnabled()) { logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]"); } Method handler = this.exceptionHandlerMap.get(exceptionClass); while (handler == null && exceptionClass != Throwable.class) { // If the corresponding exception handling method is not found for the current exception type, continue to search according to its parent class exceptionClass = exceptionClass.getSuperclass(); handler = this.exceptionHandlerMap.get(exceptionClass); } if (handler != null && logger.isTraceEnabled()) { logger.trace("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler); } return handler; } private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable { Object[] handlerArgs; // Preparation method parameters if (method.getParameterCount() == 1) { handlerArgs = new Object[] {ex}; } else { handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex}; } try { // Call exception handling method method.invoke(this.throwsAdvice, handlerArgs); } catch (InvocationTargetException targetEx) { throw targetEx.getTargetException(); } } }
AsyncExecutionInterceptor
brief introduction
AOP MethodInterceptor that uses asynctask executor to execute method calls asynchronously;
Core code
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered { @Override @Nullable public Object invoke(final MethodInvocation invocation) throws Throwable { Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); // Get the executor corresponding to the method call AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod); if (executor == null) { // The actuator must exist, or an exception will be thrown directly throw new IllegalStateException( "No executor specified and no default executor set on AsyncExecutionInterceptor either"); } // Encapsulating method calls as asynchronous tasks Callable<Object> task = () -> { try { // Call method call Object result = invocation.proceed(); if (result instanceof Future) { // Wait for return value return ((Future<?>) result).get(); } } catch (ExecutionException ex) { // exception handling handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments()); } catch (Throwable ex) { // exception handling handleError(ex, userDeclaredMethod, invocation.getArguments()); } return null; }; // Submit asynchronous task return doSubmit(task, executor, invocation.getMethod().getReturnType()); } }
CacheInterceptor
brief introduction
AOP MethodInterceptor supporting Spring declarative cache management;
Core code
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable { @Override @Nullable public Object invoke(final MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); // Encapsulating method calls as cache operation calls CacheOperationInvoker aopAllianceInvoker = () -> { try { // Call method call return invocation.proceed(); } catch (Throwable ex) { throw new CacheOperationInvoker.ThrowableWrapper(ex); } }; // Get target object Object target = invocation.getThis(); Assert.state(target != null, "Target must not be null"); try { // Execute cache operation call return execute(aopAllianceInvoker, target, method, invocation.getArguments()); } catch (CacheOperationInvoker.ThrowableWrapper th) { throw th.getOriginal(); } } }
TransactionInterceptor
brief introduction
AOP MethodInterceptor supporting Spring declarative transaction management;
Core code
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { @Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() { @Override @Nullable public Object proceedWithInvocation() throws Throwable { // Call method call return invocation.proceed(); } @Override public Object getTarget() { return invocation.getThis(); } @Override public Object[] getArguments() { return invocation.getArguments(); } }); } }
AbstractTraceInterceptor
brief introduction
The basic method interceptor implementation of log tracking;
Core code
public abstract class AbstractTraceInterceptor implements MethodInterceptor, Serializable { @Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { // Get Log Log logger = getLoggerForInvocation(invocation); if (isInterceptorEnabled(invocation, logger)) { // Log trace call return invokeUnderTrace(invocation, logger); } else { // Call method call return invocation.proceed(); } } // The implementation subclass ensures that the method call must be called, and log tracking can be performed before and after the method call @Nullable protected abstract Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable; }
ExposeInvocationInterceptor
brief introduction
Put the current MethodInvocation into the ThreadLocalMap corresponding to the current thread as a thread local variable;
If used, the ExposeInvocationInterceptor is normally at the top of the interceptor chain;
Core code
public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable { // Single case public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor(); private static final ThreadLocal<MethodInvocation> invocation = new NamedThreadLocal<>("Current AOP method invocation"); // Get current MethodInvocation public static MethodInvocation currentInvocation() throws IllegalStateException { MethodInvocation mi = invocation.get(); if (mi == null) { throw new IllegalStateException( "No MethodInvocation found: Check that an AOP invocation is in progress and that the " + "ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " + "advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! " + "In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " + "must be invoked from the same thread."); } return mi; } // Ensure singleton private ExposeInvocationInterceptor() { } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { // Cache current MethodInvocation MethodInvocation oldInvocation = invocation.get(); // Update current MethodInvocation invocation.set(mi); try { // Call method call return mi.proceed(); } finally { // Restore current MethodInvocation invocation.set(oldInvocation); } } }
DelegatingIntroductionInterceptor
brief introduction
AOP MethodInterceptor supporting introduction function;
Core code
public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport implements IntroductionInterceptor { @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { if (isMethodOnIntroducedInterface(mi)) { Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments()); // If the return value is the delegate itself, the proxy should be the one you really want to return if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) { Object proxy = ((ProxyMethodInvocation) mi).getProxy(); if (mi.getMethod().getReturnType().isInstance(proxy)) { retVal = proxy; } } return retVal; } return doProceed(mi); } @Nullable protected Object doProceed(MethodInvocation mi) throws Throwable { // Call method call return mi.proceed(); } }