SourceCode Java SpringBoot Spring Code

Spring AOP源码分析

Posted on 2022-05-26,10 min read
  • 以前一直听八股文说Spring的AOP是基于动态代理实现的。所以这里分析一下SpringAOP的源码。
  • 注:本文基于SB 2.4.3版本进行分析,其他版本可能代码会有所出入。同时,由于JDK动态代理和CGLIB动态代理在SpringAop中的实现大差不差,所以这里只分析JDK动态代理。

入口分析

  • 在我们手动或自动调用Spring的getBean方法的时候,一路往下跟,会跟到ProxyCreatorSupport#createAopProxy这个方法上:

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }
    
  • 这里的getAopProxyFactory方法实际上返回的是AopProxyFactory的实例对象。

  • 所以我们定位到这个方法AopProxyFactory#createAopProxy,它有一个唯一的实现为:DefaultAopProxyFactory。其中代码如下:

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!NativeDetector.inNativeImage() &&
            (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                                             "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                // 使用JDK动态代理
                return new JdkDynamicAopProxy(config);
            }
            // 使用CGlib动态代理
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            // 使用JDK动态代理
            return new JdkDynamicAopProxy(config);
        }
    }
    
  • 我们可以看到,它实际上就是根据目标对象的一个状态来选择使用JDK动态代理(实现为:JdkDynamicAopProxy)还是使用CGlib动态代理(实现为:CglibAopProxyObjenesisCglibAopProxy,其中ObjenesisCglibAopProxy继承CglibAopProxy)。

JDK动态代理

  • JdkDynamicAopProxy的类签名为:

    final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {}
    
  • 可以看到,其实现了一个InvocationHandler的接口,而这个接口就是实现JDK动态代理的那个接口。

  • 获取代理的方法为:

    @Override
    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }
    
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
    }
    
  • 毫无疑问,就是JDK动态代理的那一套,那么我们就将注意力放在了它是如何实现InvocationHandler接口的。

    @Override
    @Nullable
    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;
    
        try {
            // 位置1,此处不必过多关注。
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // 调用的方法为equals,并且被代理的类没有实现equals方法,则通过此处实现的equals来判断。
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // 调用的方法为hashCode,并且被代理的类没有实现hashCode方法,则通过此处实现的equals来判断。
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
    			// 如果调用的方法是DecoratingProxy中的方法,因为其中只有一个getDecoratedClass方法,这里直接返回被装饰的Class即可
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    			// 代理不是不透明的,且是接口中声明的方法,且是Advised或其父接口的方法,则直接调用构造时传入的advised对象的相应方法
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }
    
            Object retVal;
    		// 位置2,此处需要关注。
            if (this.advised.exposeProxy) {
    		    // 如果暴露代理,则用AopContext保存当前代理对象。用于多级代理时获取当前的代理对象,一个有效应用是同类中调用方法,代理拦截器会无效。可以使用AopContext.currentProxy()获得代理对象并调用。
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
    
            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);
    
            // 位置3,此处需要关注。
            // 获取配置在当前方法的MethodInterceptor实现类列表。
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            if (chain.isEmpty()) {
                // 如果链表为空,则直接调用具体方法。
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // 否则去调用interceptor链,然后调用实际方法。
                // We need to create a method invocation...
                MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }
    		// 位置4,返回值特殊处理,不必过多关注。
            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                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);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            // 位置5,此处需要关注。
            // 将AopContext设置为原先的proxy。
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
    
  • 位置2和位置5:这里就分别对应了AopContext的设置和取消设置。

    • 我们一种解决this调用导致事务失效的方法就是通过AopContext.currentProxy()来获取当前对象的代理对象,但是我们需要配置:@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)否则就会导致空指针异常,原因就是在这里,如果没有设置exposeProxy = true的话,这里根本就不会把当前代理设置到AopContext中的ThreadLocal中,所以你在获取的时候自然就报了空指针异常了。
  • 位置3:

    • 这里我们注意到变量名为chain,意思为链。所以这里实际上同时还使用了一个责任链模式来调用MethodInterceptor

SpringAOP中的责任链模式

  • 上面,我们分析到,SpringAop除了使用了动态代理模式以外,还使用到了责任链模式(不了解责任链模式的请翻看我以前的文章)。

  • 但是这里除了变量名以外可能第一时间不好发现这里是个责任链模式。为什么呢?因为责任链模式的实现,至少是需要得有个循环或者迭代吧?但是这里都没有明显的关键词,所以不好发现这里的迭代器模式是怎么实现的。

  • 这里可以自己打断点去看怎么实现的,这里只说结论。

  • 这里的责任链的模式实现,实际上是通过一个递归来实现的。

  • 我们定位到执行的那段方法:

    // We need to create a method invocation...
    MethodInvocation invocation =
        new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // Proceed to the joinpoint through the interceptor chain.
    retVal = invocation.proceed();
    
  • 关注ReflectiveMethodInvocation这个类,然后对部分内容进行提取。

    public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
        /*忽略其他代码*/
        // 当前执行到的interceptor的索引
        private int currentInterceptorIndex = -1;
        // interceptor组成的拦截链
        protected final List<?> interceptorsAndDynamicMethodMatchers;
        /*忽略其他代码*/
        public Object proceed() throws Throwable {
            // We start with an index of -1 and increment early.
            // 位置1
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                // 执行实际的方法
                return invokeJoinpoint();
            }
    		// 位置2
            Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    			/*忽略其他代码*/
            }
            else {
                // 位置3
                // 执行拦截器方法。
                return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }
        
    }
    
  • 这样来看就比较明显了,其中:

    • 位置1的代码,是整个递归的方法出口,即调用实际的方法。

    • 位置2的代码,是从拦截链(interceptorsAndDynamicMethodMatchers)里面根据currentInterceptorIndex取出当前的拦截器,并且currentInterceptorIndex会+1,下一次获取的时候就是责任链中的下一个了。

    • 位置3调用实际方法的时候,是传入了一个this的,然后我们去看框架中内置的一些AOP的实现,会发现都是类似这样的一个结构:

      public Object invoke(MethodInvocation invocation) throws Throwable {
      	/*做些前置处理*/
          Object retVal = invocation.proceed();
          /*做些后置处理*/
          return retVal;
      }
      
    • 而其中的invocation,也就是上一步中传入的this,在上一步中的currentInterceptorIndex是+1了的,所以此处执行的就是下一个,自然而然的就通过一个递归实现了责任链模式。

  • 不好理解的话就看下面:

    interceptorsAndDynamicMethodMatchers中有 Interceptor1,Interceptor2,Interceptor3
    
    那么当调用到实际方法的调用栈的情况如下:
    执行到第一个:
    -Interceptor1#invoke(currentInterceptorIndex = -1)
    执行到第二个:
    --Interceptor2#invoke(currentInterceptorIndex = 0)
    -Interceptor1#invoke(currentInterceptorIndex = 0)
    执行到第三个:
    ---Interceptor2#invoke(currentInterceptorIndex = 1)
    --Interceptor2#invoke(currentInterceptorIndex = 1)
    -Interceptor1#invoke(currentInterceptorIndex = 1)
    执行到实际方法:
    ----invokeJoinpoint(currentInterceptorIndex = 2)
    ---Interceptor2#invoke(currentInterceptorIndex = 2)
    --Interceptor2#invoke(currentInterceptorIndex = 2)
    -Interceptor1#invoke(currentInterceptorIndex = 2)
    

总结

  • 通过对AOP源码的分析,我们能够总结出如下知识点:
    • AopContext是基于ThreadLocal来进行实现的,并且必须要配置@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true),不然在代码中获取的时候可能会导致空指针。
    • Spring的AOP除了使用了代理模式以外,还使用了责任链模式。
    • Spring中的AOP总体来说是基于代理模式实现的,所以根据具体使用的CGLIB还是JDK代理,需要满足对应的条件,否则,AOP是会失效的。

下一篇: Java日志记录工具→

loading...
��