首页 > 编程语言 >【Spring AOP】【十】Spring AOP源码解析-讲一下ExposeInvocationInterceptor

【Spring AOP】【十】Spring AOP源码解析-讲一下ExposeInvocationInterceptor

时间:2023-02-23 07:22:05浏览次数:40  
标签:return Spring ExposeInvocationInterceptor 源码 MethodInvocation AOP invocation mi

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我们就介绍到这里,有理解不对的地方欢迎指正哈。

标签:return,Spring,ExposeInvocationInterceptor,源码,MethodInvocation,AOP,invocation,mi
From: https://www.cnblogs.com/kukuxjx/p/17146602.html

相关文章

  • spring boot内置tomcat运行JSP报错
    =============================================== 2023/2/23_第1次修改                       粽先生 ==================================......
  • SpringBoot
    是什么SpringBoot基于Spring框架之上的一个微服务架构开发框架大大简化了Spring的开发。因为SpringBoot提供了大量的自动配置。而且它是基于Java配置方式的开发(全注解)Sp......
  • 【Spring AOP】【九】Spring AOP源码解析-拦截器/通知器链的执行过程
    1 前言上一节我们说了通知器链的而顺序问题,那么这节我们该看看它的执行了。我们拿JDK代理的执行过程来看哈。2 源码分析2.1 方法通读对于JDK动态代理的,那我们再......
  • C# AOP实现方式
    一、AOP概念  官方解释:AOP(Aspect-OrientedProgramming,面向切面编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一......
  • fusion app自定义事件源码介绍(下)
    安装apk安装APK(filePath)lua源码function安装APK(filePath)localintent=Intent(Intent.ACTION_VIEW);intent.addCategory("android.intent.category.DEFAU......
  • Spring IOC官方文档学习笔记(十二)之基于Java的容器配置
    1.@Bean与@Configuration(1)标注于类之上的@Configuration注解与标注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解标注的方法用于实例化bean并将其......
  • SpringBean的生命周期
    springBean的生命周期实例化阶段spring框架取出beanDefinition之前会判断信息进行判断当前bean的范围是否是单例,是否延迟加载,是否是factoryBean等,然后再通过反射进行实......
  • 【深度挖掘 RocketMQ底层源码】「底层源码挖掘系列」抽丝剥茧贯穿RocketMQ的消费者端
    承接【【深度挖掘RocketMQ底层源码】「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(Pull模式-上)】pullBlockIfNotFound方法通过该方法获取该Message......
  • 【Spring AOP】【八】Spring AOP源码解析-拦截器/通知器链的执行顺序
    1 前言上节我们看过了代理对象执行方法的大致过程,我们留着通知器链的具体执行没说,这节我们先讲解一下通知器的执行顺序。通知器或者叫拦截器,叫法不一样,这里我们还是都......
  • spring-申明式事务
    一.什么是事务事务的原则是:处于事务中的sql代码块会保持一致状态,即要么都能改变数据库,要么都不能改变数据库ACID原则:原子性一致性隔离性多个业务可能操作同一个业......