2021-05-27 23:43  阅读(237)
文章分类:Spring 源码解析 文章标签:SpringSpring 源码
©  原文作者:Java学习部落 原文地址:https://www.zhihu.com/people/tobetopjavaer/posts

本文将带领大家对Spring Aop的源码进行详细阅读,可能会有点晕车,但是希望大家能咬牙坚持,可能多看几遍你就融汇贯通了,那种被人打开任督二脉的感觉,我试过,不知道有多爽!!!

进入正题

寻找入口

Spring 的 AOP 是通过接入 BeanPostProcessor 后置处理器开始的,它是 Spring IOC 容器经常使用到的一个特性,这个 Bean 后置处理器是一个监听器,可以监听容器触发的 Bean 声明周期事件。

后置处理器向容器注册以后,容器中管理的 Bean 就具备了接收 IOC 容器事件回调的能力。

BeanPostProcessor 的使用非常简单,只需要提供一个实现接口 BeanPostProcessor 的实现类,然后在 Bean 的配置文件中设置即可。

1、BeanPostProcessor 源码

202105272343074171.png

这两个回调的入口都是和容器管理的 Bean 的生命周期事件紧密相关,可以为用户提供在 Spring IOC容器初始化 Bean 过程中自定义的处理操作。

2、AbstractAutowireCapableBeanFactory 类对容器生成的 Bean 添加后置处理器

BeanPostProcessor 后置处理器的调用发生在 Spring IOC 容器完成对 Bean 实例对象的创建和属性的依赖注入完成之后。

在对Spring依赖注入的源码分析过程中我们知道,当应用程序第一次调用 getBean()方法(lazy-init 预实例化除外)向 Spring IOC 容器索取指定 Bean 时,触发 Spring IOC 容器创建 Bean 实例 对 象 并 进 行 依 赖 注 入 的 过 程 , 其 中 真 正 实 现 创 建 Bean 对 象 并 进 行 依 赖 注 入 的 方 法 是 AbstractAutowireCapableBeanFactory 类的 doCreateBean()方法,主要源码如下:

202105272343075963.png202105272343077565.png

从上面的代码中我们知道,为 Bean 实例对象添加 BeanPostProcessor 后置处理器的入口的是initializeBean()方法。

3、initializeBean()方法为容器产生的 Bean 实例对象添加 BeanPostProcessor 后置处理器

同样在 AbstractAutowireCapableBeanFactory 类中,initializeBean()方法实现为容器创建的 Bean实例对象添加 BeanPostProcessor 后置处理器,源码如下:

202105272343079277.png202105272343081029.png2021052723430855511.png

BeanPostProcessor 是一个接口,其初始化前的操作方法和初始化后的操作方法均委托其实现子类来实现,在 Spring 中,BeanPostProcessor 的实现子类非常的多,分别完成不同的操作,

如:AOP 面向切面编程的注册通知适配器、Bean 对象的数据校验、Bean 继承属性、方法的合并等等,我们以最简单的 AOP 切面织入来简单了解其主要的功能。

下面我们来分析其中一个创建 AOP 代理对象的子类 AbstractAutoProxyCreator 类。

该类重写了 postProcessAfterInitialization()方法。

选择代理策略

进入 postProcessAfterInitialization()方法,我们发现调到了一个非常核心的方法 wrapIfNecessary(),

其源码如下:

2021052723430875613.png2021052723430888215.png2021052723430940717.png2021052723430988419.png

整个过程跟下来,我发现最终调用的是 proxyFactory.getProxy()方法。到这里我们大概能够猜到 proxyFactory 有 JDK 和 CGLib 的,那么我们该如何选择呢?最终调用的是 DefaultAopProxyFactory的 createAopProxy()方法:

2021052723430997621.png

调用代理方法

分析调用逻辑之前先上类图,看看 Spring 中主要的 AOP 组件:

2021052723431029523.png

上面我们已经了解到 Spring 提供了两种方式来生成代理方式有 JDKProxy 和 CGLib。下面我们来研究 一下 Spring 如何使用 JDK 来生成代理对象,具体的生成代码放在 JdkDynamicAopProxy 这个类中, 直接上相关代码:

    /**
    * 获取代理类要实现的接口,除了 Advised 对象中配置的,还会加上 SpringProxy, Advised(opaque=false) 
    * 检查上面得到的接口中有没有定义 equals 或者 hashcode 的接口 
    * 调用 Proxy.newProxyInstance 创建代理对象 
    */ 
    @Override 
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) { 
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); 
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); 
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); 
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 
    }

通过注释我们应该已经看得非常明白代理对象的生成过程,此处不再赘述。

下面的问题是,代理对象生成了,那切面是如何织入的?

我们知道 InvocationHandler 是 JDK 动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。

而从 JdkDynamicAopProxy 的源码我们可以看到这个类其实也实现了 InvocationHandler,下面我们分析 Spring AOP 是如何织入切面的,直接上源码看 invoke()方法:

2021052723431050625.png2021052723431105327.png

主要实现思路可以简述为:首先获取应用到此方法上的通知链(Interceptor Chain)。

如果有通知,则应用通知,并执行 JoinPoint;如果没有通知,则直接反射执行 JoinPoint。

而这里的关键是通知链是如何获取的以及它又是如何执行的呢?

现在来逐一分析。

首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现逻辑

2021052723431150229.png

通过上面的源码我们可以看到,实际获取通知的实现逻辑其实是由 AdvisorChainFactory 的 getInterceptorsAndDynamicInterceptionAdvice()方法来完成的,且获取到的结果会被缓存。

下面来分析 getInterceptorsAndDynamicInterceptionAdvice()方法的实现:

2021052723431163631.png2021052723431182833.png

这个方法执行完成后,Advised 中配置能够应用到连接点(JoinPoint)或者目标类(Target Object)的 Advisor 全部被转化成了 MethodInterceptor,接下来我们再看下得到的拦截器链是怎么起作用的。

    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(); 
    }

从 这 段 代 码 可 以 看 出 , 如 果 得 到 的 拦 截 器 链 为 空 , 则 直 接 反 射 调 用 目 标 方 法 , 否 则 创 建MethodInvocation,调用其 proceed()方法,触发拦截器链的执行,来看下具体代码:

2021052723431227735.png

至此,通知链就完美地形成了。

我们再往下来看 invokeJoinpointUsingReflection()方法,其实就是反射调用:

    public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, 
    Object[] args) 
        throws Throwable { 
        // Use reflection to invoke the method. 
            try { 
                ReflectionUtils.makeAccessible(method);
                return method.invoke(target, args); 
            }
            catch (InvocationTargetException ex) { 
                // Invoked method threw a checked exception. 
                // We must rethrow it. The client won't see the interceptor. 
                throw ex.getTargetException(); 
            }
        catch (IllegalArgumentException ex) { 
            throw new AopInvocationException("AOP configuration seems to be invalid: tried calling 
              method [" +
              method + "] on target [" + target + "]", ex); 
        }
        catch (IllegalAccessException ex) { 
             throw new AopInvocationException("Could not access method [" + method + "]", ex); 
        } 
    }

Spring AOP 源码就分析到这儿,相信小伙伴们应该有了基本思路,下面时序图来一波。

(需要高清的找我)

2021052723431248337.png

触发通知

在为 AopProxy 代理对象配置拦截器的实现中,有一个取得拦截器的配置过程,这个过程是由 DefaultAdvisorChainFactory 实现的,这个工厂类负责生成拦截器链,在它的getInterceptorsAndDynamicInterceptionAdvice 方法中,有一个适配器和注册过程,通过配置 Spring 预先设计好的拦截器,Spring 加入了它对 AOP 实现的处理。

    /**
    * 从提供的配置实例 config 中获取 advisor 列表,遍历处理这些 advisor.如果是 IntroductionAdvisor, 
    * 则判断此 Advisor 能否应用到目标类 targetClass 上.如果是 PointcutAdvisor,则判断 
    * 此 Advisor 能否应用到目标方法 Method 上.将满足条件的 Advisor 通过 AdvisorAdaptor 转化成 Interceptor 列表返回. 
    */ 
    @Override 
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice( 
        Advised config, Method Method, @Nullable Class<?> targetClass) { 
        List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length); 
        Class<?> actualClass = (targetClass != null ? targetClass : Method.getDeclaringClass()); 
        //查看是否包含 IntroductionAdvisor 
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); 
        //这里实际上注册一系列 AdvisorAdapter,用于将 Advisor 转化成 MethodInterceptor 
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); 
        ...
        return interceptorList; 
    }

GlobalAdvisorAdapterRegistry 负责拦截器的适配和注册过程。

2021052723431338739.png

而 GlobalAdvisorAdapterRegistry 起到了适配器和单例模式的作用,提供了一个 DefaultAdvisorAdapterRegistry,它用来完成各种通知的适配和注册过程。

2021052723431358941.png2021052723431365143.png2021052723431402145.png

DefaultAdvisorAdapterRegistry 设置了一系列的是配置,正是这些适配器的实现,为 Spring AOP 提供了编织能力。下面以 MethodBeforeAdviceAdapter 为例,看具体的 实现:

2021052723431438647.png

Spring AOP 为了实现 advice 的织入,设计了特定的拦截器对这些功能进行了封装。

我们接着看 MethodBeforeAdviceInterceptor 如何完成封装的?

    public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { 
        private MethodBeforeAdvice advice; 
        /**
        * Create a new MethodBeforeAdviceInterceptor for the given advice. 
        * @param advice the MethodBeforeAdvice to wrap 
        */ 
        public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { 
            Assert.notNull(advice, "Advice must not be null"); 
            this.advice = advice; 
        }
        @Override 
        public Object invoke(MethodInvocation mi) throws Throwable { 
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); 
            return mi.proceed(); 
        } 
    }

可以看到,invoke 方法中,首先触发了 advice 的 before 回调,然后才是 proceed。

AfterReturningAdviceInterceptor 的源码:

2021052723431457049.png

ThrowsAdviceInterceptor 的源码:

2021052723431474751.png

至此,我们知道了对目标对象的增强是通过拦截器实现的,最后还是上时序图:

2021052723431498653.png

点赞(1)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 《Spring源码解析(九)》从源码深处体验Spring核心技术--详细解读Spring AOP源码
上一篇
《Spring源码解析(八)》从源码深处体验Spring核心技术--AOP那些你不得不明白的概念
下一篇
《Spring源码解析(十)》从源码深处体验Spring核心技术--SpringMVC流程和九大组件