【Spring AOP】【十】Spring AOP源码解析-讲一下ExposeInvocationInterceptor
1 前言
不知道你在调试的时候,有没有发现我们的通知器链上首个元素会给我放进来一个ExposeInvocationInterceptor类型的通知器,看下图是不是,我们在之前其实也说过一次只是一句话带过了,那我们本节看下它的进场时机和具体是做什么的,可以怎么做。
2 源码分析
2.1 进场时机
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) { // 如果通知器列表是一个空列表,直接返回 if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { // 检测 每个通知器advisor是否存在类型是否正确 if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; break; } } // 往头部添加了一个默认的通知器DefaultPointcutAdvisor 我猜的哈 我怀疑是多个通知器组成链表 这个充当头吧 我猜的哈 !!!这是我上次猜的哈, 我这个猜的不对哈 if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { advisors.add(0, ExposeInvocationInterceptor.ADVISOR); return true; } } return false; }
2.2 ExposeInvocationInterceptor源码
我们看了它的进场时机,那让我们看下这个类是干啥的(看源码看这个类的注释)源码如下:
/** * Interceptor that exposes the current {@link org.aopalliance.intercept.MethodInvocation} * as a thread-local object. We occasionally need to do this; for example, when a pointcut * (e.g. an AspectJ expression pointcut) needs to know the full invocation context. * * <p>Don't use this interceptor unless this is really necessary. Target objects should * not normally know about Spring AOP, as this creates a dependency on Spring API. * Target objects should be plain POJOs as far as possible. * * <p>If used, this interceptor will normally be the first in the interceptor chain. * * 意思就是 暴漏我们的通知器链对象MethodInvocation,保存在本地线程里 * 当我们需要的完整的调用上下文时,就可以ExposeInvocationInterceptor.currentInvocation()获取到 * 除非确实需要,否则不要使用此拦截器。 * 通常不了解SpringAOP,因为这会创建对SpringAPI的依赖,目标对象应尽可能是普通POJO。 * 如果使用,这个拦截器通常是拦截器链中的第一个。 * @author Rod Johnson * @author Juergen Hoeller */ @SuppressWarnings("serial") public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable { /** Singleton instance of this class. */ public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor(); /** * Singleton advisor for this class. Use in preference to INSTANCE when using * Spring AOP, as it prevents the need to create a new Advisor to wrap the instance. */ public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) { @Override public String toString() { return ExposeInvocationInterceptor.class.getName() +".ADVISOR"; } }; // 本地线程保存 private static final ThreadLocal<MethodInvocation> invocation = new NamedThreadLocal<>("Current AOP method invocation"); /** * Return the AOP Alliance MethodInvocation object associated with the current invocation. * @return the invocation object associated with the current invocation * @throws IllegalStateException if there is no AOP invocation in progress, * or if the ExposeInvocationInterceptor was not added to this interceptor chain */ 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; } private ExposeInvocationInterceptor() { } @Override @Nullable public Object invoke(MethodInvocation mi) throws Throwable { // 先获取到旧值 刚开始其实这里oldInvocation就是空 MethodInvocation oldInvocation = invocation.get(); // 把我们的通知器链对象放进本地线程 invocation.set(mi); try { // 继续执行通知器链 return mi.proceed(); } finally { // 在最后通知器链执行完毕的时候,设置回去oldInvocation 也就是空 invocation.set(oldInvocation); } } @Override public int getOrder() { /** * int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; * 通知器的排序值 * 困惑么 Integer最小值+1 那我们普通的通知器默认的排序值不是Integer最小值么 * 它比我们的默认值大,说明他的优先级应该低啊 那岂不是在排序的时候就会放最后一个 怎么还能在第一个呢?? * 看它实现的接口 PriorityOrdered 他有这个标识 所以它是老大 */ return PriorityOrdered.HIGHEST_PRECEDENCE + 1; } /** * Required to support serialization. Replaces with canonical instance * on deserialization, protecting Singleton pattern. * <p>Alternative to overriding the {@code equals} method. */ private Object readResolve() { return INSTANCE; } }
可以看到其实就是把我们的通知器链对象,放进了本地线程里,然后后续的通知器里就可以根据ExposeInvocationInterceptor.currentInvocation()来得到我们的通知器链对象,并且实现的是PriorityOrdered,排面拉满第一个,我们的后置通知,其实就用到了,我们看看:
public Object invoke(MethodInvocation mi) throws Throwable { try { // 继续下一个通知器 return mi.proceed(); } finally { // 调用通知方法 invokeAdviceMethod(getJoinPointMatch(), null, null); } } protected JoinPointMatch getJoinPointMatch() { // 看这里是不是用到了 MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation(); if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } return getJoinPointMatch((ProxyMethodInvocation) mi); }
3 小结
好了关于ExposeInvocationInterceptor我们就介绍到这里,有理解不对的地方欢迎指正哈。