手把手分析Spring AOP基于JDK动态代理的实现

685 阅读18分钟

1、基础概念

在扒SpringAOP的diku之前,咱们还是老生常谈的介绍一下关于AOP的一些基础概念:

  • 目标对象(target):被增强的对象
  • 连接点(JoinPoint):被增强方法的增强点,即被增强方法的before、after return、throw exception位置。
  • 通知(Advice):增强逻辑
  • 切入点(Pointcut ):指定增强逻辑需要增强在哪个对象的哪个方法上。可以简单理解为pointcut = target + joinpoint。
  • 顾问(Advisor):Advisor其实是Spring AOP代码结构上的概念,Advisor是Pointcut和Advice的封装,一个Advisor可以清楚的描述需要在哪个地方(Pointcut)进行什么增强(Advice)。Advisor = Pointcut + Advice。
  • 切面(Aspect):相当于Advisor,也是Pointcut + Advice,另一种概念而已。一个切面能够清楚的描述需要在哪个地方(Pointcut)进行什么增强(Advice)。
  • 代理对象(Proxy):最终生成的增强对象

2、AOP使用方式

网络上的博客大部分都说AOP的使用方法有:基于经典代理AOP实现、基于纯POJO切面式的实现等等。从使用层面上分析,确实可以分为这些使用类型。

而我们今天要说的,是从底层原理上分析,这么看其实AOP的实现方式仅有三种情况,这三种情况分别对应三个实现类ProxyFactoryProxyFactoryBeanAspectJProxyFactory。这三个类与父类的继承关系如下:

classDiagram
  class ProxyFactory
  class ProxyFactoryBean
  class AspectJProxyFactory
  class ProxyCreatorSupport
  class AdvisedSupport
  class ProxyConfig
  class Advised
  
  ProxyFactory --|> ProxyCreatorSupport
  ProxyFactoryBean--|> ProxyCreatorSupport
  AspectJProxyFactory--|> ProxyCreatorSupport
  ProxyCreatorSupport --|> AdvisedSupport
  AdvisedSupport --|> ProxyConfig
  AdvisedSupport ..|> Advised

下面,我们从上往下分别简单介绍下这些类或接口的作用:

  • ProxyFactory:允许用户完全脱离Spring容器构造动态代理类。即手动构造Pointcut、Advice以及Advisor。
  • ProxyFactoryBean:依赖Spring容器,用户要将切点、通知、顾问等AOP相关的类注入Spring中,然后依赖ProxyFactoryBean构造单个动态代理类。
  • AspectJProxyFactory:以切面的视角构造动态代理类,我们只需要创建简单的pojo,然后通过aop:config标签或者AspectJ注解实现大范围动态代理。
  • ProxyCreatorSupportProxyFactoryProxyFactoryBeanAspectJProxyFactory的公共父类,其中包含了一些公共属性和方法,例如aopProxyFactory、createAopProxy()等。
  • AdvisedSupportProxyCreatorSupport的父类,其中包含了advice、advisor的配置与操作方法,根据名字也可以很容易了解,AdvisedSupport是关于advice操作支持的类。
  • ProxyConfigAdvisedSupport的父类,主要存储了代理相关的配置,比如是否冻结、是否暴露到当前线程中等。
  • AdvisedAdvisedSupport的接口,规范了AdvisedSupport的一系列行为。

对于ProxyFactoryProxyFactoryBeanAspectJProxyFactory三类代理工厂,从作者的视角去看,这三类工厂具备有不同的上层相同的下层

不同的上层表示这三种类关于获取Pointcut、Advice等重要AOP配置的方式是不同的。比如ProxyFactory完全是用户手动构建然后通过addxxx方法将配置注入。而ProxyFactoryBeanAspectJProxyFactory便依赖了Spring容器强大的IOC功能,帮助用户自动注入Pointcut和Advice等,用户只需要关注核心切面逻辑即可。

相同的下层表示Spring的AOP代理最终还是两种实现,一种是JDK的动态代理实现;另外一种是Cglib的动态代理实现。因此,上述三种AOP的使用方式最终都会创建出CglibAopProxy/JdkDynamicAopProxy,进而通过这些AopProxy最终获得代理类。

我们可以用下图来形象描述上面的意思。

image.png

接下来便会对这三种AOP的使用方式中的ProxyFactory、JdkDynamicAopProxy进行源码层面(扒diku)的分析。

3、ProxyFactory底层原理

ProxyFactory的使用一般过程如下所示,主要使用步骤分为这几步:

image.png

  • 创建代理工厂
  • 配置需要代理类
  • 配置advice或者advisor,前者会默认使用不过滤的pointcut,后者我们可以配置自定义的pointcut
  • 获取代理类,执行相应方法,观察是否代理成功
// ProxyFactoryTest.java
public class ProxyFactoryTest {
    public static void main(String[] args) {
        // 1、创建代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 2、配置需要代理类
        proxyFactory.setTarget(new TargetClass());
        // 3、配置advice。proxyFactory中最终仅保存了advisor,直接添加advice最终也会转化成advisor,只不过其中的pointcut不过进行任何过滤
        proxyFactory.addAdvice(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("MethodBeforeAdvice before");
            }
        });
        // 4、配置advisor。我们可以使用PointcutAdvisor接口配置匿名advisor,在实现的方法中,我们可以指定advice增强和pointcut切点
        proxyFactory.addAdvisor(new PointcutAdvisor() {
            @Override
            public Advice getAdvice() {
                return new AfterReturningAdvice() {
                    @Override
                    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
                        System.out.println("returning");
                    }
                };
            }

            @Override
            public boolean isPerInstance() {
                return true;
            }

            @Override
            public Pointcut getPointcut() {
                NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
                nameMatchMethodPointcut.setMappedName("print2");
                return nameMatchMethodPointcut;
            }
        });
        // 5、完成配置后,可以使用getProxy()方法获取代理类
        TargetClass proxy = (TargetClass) proxyFactory.getProxy();
        // 6、执行该方法便能够看到已经被增强了
        proxy.print();
        System.out.println("----------------------");
        proxy.print2();
    }
}

class TargetClass {
    public void print() {
        System.out.println("TargetClass print");
    }

    public void print2() {
        System.out.println("TargetClass print2");
    }
}

大家可以把这个demo拷贝到本地运行试试,运行结果和下面展示的应该没有差别。

MethodBeforeAdvice before
TargetClass print
----------------------
MethodBeforeAdvice before
TargetClass print2
returning

下面我们将针对其中的advice、advisor配置proxy代理获取的核心步骤进行详细的源码分析,其余部分都非常简单,大家自行查阅即可。

3.1、Advice的配置

Advice配置的基本代码如下所示,我们可以配置各种功能的增强,比如在进入方法前的增强(MethodBeforeAdvice)、方法返回后的增强(AfterReturningAdvice)等。这些增强之后会根据pointcut被横切到需要代理的目标类上。

// ProxyFactoryTest.java
proxyFactory.addAdvice(new MethodBeforeAdvice() {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("MethodBeforeAdvice before");
    }
});

addAdvice()方法调用链路如下所示。主要都在AdvisedSupport这个类中,上面我们分析继承关系时也说到了AdvisedSupport主要包含了advice、advisor的配置与操作方法。

@Override
public void addAdvice(Advice advice) throws AopConfigException {
    // 获取当前已经配置的advisor数量
    int pos = this.advisors.size();
    // 添加advice
    addAdvice(pos, advice);
}
@Override
public void addAdvice(int pos, Advice advice) throws AopConfigException {
    Assert.notNull(advice, "Advice must not be null");
    if (advice instanceof IntroductionInfo) {
        // We don't need an IntroductionAdvisor for this kind of introduction:
        // It's fully self-describing.
        addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
    } else if (advice instanceof DynamicIntroductionAdvice) {
        // We need an IntroductionAdvisor for this kind of introduction.
        throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
    } else {
        // [1] 大部分情况都是将advice封装成DefaultPointcutAdvisor,然后添加
        addAdvisor(pos, new DefaultPointcutAdvisor(advice));
    }
}

@Override
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
    if (advisor instanceof IntroductionAdvisor) {
        validateIntroductionAdvisor((IntroductionAdvisor) advisor);
    }
    // 具体的添加方法
    addAdvisorInternal(pos, advisor);
}

private List<Advisor> advisors = new ArrayList<>();
private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
    Assert.notNull(advisor, "Advisor must not be null");
    // [2] 被冻结的ProxyFactory不允许添加advisor
    if (isFrozen()) {
        throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
    }
    // outbond异常
    if (pos > this.advisors.size()) {
        throw new IllegalArgumentException(
            "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
    }
    // 添加进advisors列表中
    this.advisors.add(pos, advisor);
    // [3] 清理缓存
    adviceChanged();
}

private transient Map<MethodCacheKey, List<Object>> methodCache;
protected void adviceChanged() {
    // 清空map
    this.methodCache.clear();
}

这个调用链路其实就是把用户添加的advice封装成DefaultPointcutAdvisor,然后添加到ProxyFactory里的advisors列表中。advisors列表最终会依次增强到目标类,也就是target中。

这里有三点补充下:

  • [1] DefaultPointcutAdvisor:这是我们使用最为频繁的advisor,从命名中就可以看出,这个advisor持有pointcut(切向哪)和advice(增强什么)两个重要配置类。**这个advisor(顾问,也可以理解为切面)能够详细描述出在哪个切点上进行怎样的增强。**如果我们在构造时仅传入advice配置,那么pointcut配置就默认使用Pointcut.TRUE,表示能够匹配所有的类和方法,即能够增强任何需要增强类和方法,不进行过滤。
  • [2][3] 冻结和缓存要一起搭配来看,因为只有被冻结的ProxyFactory,methodCache才能保持不变,不然每有一个advisor添加,缓存都会被清空。被冻结后,ProxyFactory会做一个小优化,即缓存method与advisor的映射,后期代理类每一次调用增强方法时,会直接从缓存中获取增强逻辑,不需要每次遍历advisors列表,判断advisor持有的pointcut是否满足method的过滤条件。

最后再总结下:Advice可以使用addAdvice()方法进行配置,这会向ProxyFactory中的advisors列表中append一个DefaultPointcutAdvisor,这个顾问的pointcut采用Pointcut.TRUE,支持任何类和方法的增强。

此时ProxyFactory的结构类似下图所示。

image.png

至此,Advice的配置流程我们已经梳理完了。

3.2、Advisor配置

Advisor基本配置的代码如下所示,本例我们使用PointcutAdvisor接口构造匿名内部类,并且分别实现了getAdvice()getPointcut()方法用来定义Advisor所持有的advice和pointcut配置,这种方式比较灵活可控。当然,Advisor还有许多具备特殊功能的实现类,大家可以自行探索。

// ProxyFactoryTest.java
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Advice getAdvice() {
        return new AfterReturningAdvice() {
            @Override
            public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
                System.out.println("returning");
            }
        };
    }

    @Override
    public boolean isPerInstance() {
        return true;
    }

    @Override
    public Pointcut getPointcut() {
        NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        nameMatchMethodPointcut.setMappedName("print2");
        return nameMatchMethodPointcut;
    }
});

addAdvisor()方法的调用链路如下所示:

// AdvisedSupport.java
@Override
public void addAdvisor(Advisor advisor) {
    int pos = this.advisors.size();
    addAdvisor(pos, advisor);
}
@Override
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
    if (advisor instanceof IntroductionAdvisor) {
        validateIntroductionAdvisor((IntroductionAdvisor) advisor);
    }
    addAdvisorInternal(pos, advisor);
}
// ...

咦,调用链路貌似短了很多,哈哈哈,这是因为调用到addAdvisor(int pos, Advisor advisor)后,其实就与3.1重合了,接下来调用的方法一模一样。这也说明了,ProxyFactory无论是添加advice或者advisor,到最后其实都是添加advisor到advisors列表中。因为添加advice时,缺少pointcut的配置,所以就使用默认的Pointcut.TRUE匹配所有类和方法;而添加advisor时,是需要用户指定pointcut的,而这两类逻辑后半段其实是重合的,因此可以复用一部分。

graph LR;
添加配置 --> 添加advice
添加配置 --> 添加advisor

添加advice --> 使用默认Pointcut.TRUE封装DefaultPointcutAdvisor
添加advisor --> 直接封装DefaultPointcutAdvisor

使用默认Pointcut.TRUE封装DefaultPointcutAdvisor --> append至advisors列表中
直接封装DefaultPointcutAdvisor --> append至advisors列表中

至此,Advisor配置也说完了。

3.3、Proxy代理获取

有了以上的准备,我们有了需要被代理的目标类(TargetClass)、顾问列表(Advisors),其中顾问列表中的每个顾问还包含切点(pointcut)和增强逻辑(Advice)。因此,我们有了所有能够实现AOP的前置条件,接下来我们就要开始进行代理实现了。

代理的获取只需要调用如下语句,从中我们可知getProxy()是获取代理的核心方法。

// ProxyFactoryTest.java
TargetClass proxy = (TargetClass) proxyFactory.getProxy();

getProxy()方法源码如下所示,首先会创建一个AopProxy,然后再从AopProxy中获取真正的代理对象。

// ProxyFactory.java
public Object getProxy() {
    return createAopProxy().getProxy();
}

createAopProxy()方法源码如下所示,它首先获取AopProxyFactory,通过工厂创建AopProxy。AopProxyFactory的实现只有一个,即DefaultAopProxyFactory,在DefaultAopProxyFactory中我们可以看到获取的AopProxy最终只可能有两类:JdkDynamicAopProxy&ObjenesisCglibAopProxy(即CglibAopProxy),最后获取的Proxy只可能从这两个类中产生。

// ProxyCreatorSupport.java
protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        // 如果存在增强监听器的话,会在这里一一激活。由于监听器不是重点,这里不过多叙述
        activate();
    }
    // getAopProxyFactory()一般只返回DefaultAopProxyFactory,只有这一个实现类
    return getAopProxyFactory().createAopProxy(this);
}

// DefaultAopProxyFactory.java
@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) || ClassUtils.isLambdaClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

3.4、小结

上面我们介绍了ProxyFactory获取代理过程中与其他两者的不同之处,也是第二节所说的不同的上层,即配置advice、advisor,获取代理。最终我们来到了DefaultAopProxyFactory#createAopProxy方法处,这里可能会返回两种类型的AopProxy:JdkDynamicAopProxy&ObjenesisCglibAopProxy(即CglibAopProxy)。这里便是在第二节说的相同的下层,无论是ProxyFactory、ProxyFactoryBean还是AspectJProxyFactory,他们最后都会来到此处获取两类中的其中一类,然后再通过AopProxy获取具体的代理对象。下面,我们将会具体介绍JdkDynamicAopProxy获取代理的具体逻辑。

4、JdkDynamicAopProxy获取代理

JdkDynamicAopProxy获取代理的过程非常简单,源码如下所示:

// JdkDynamicAopProxy.java
@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);
}

看到Proxy.newProxyInstance是不是非常熟悉,其实就是JDK动态代理的过程。但这里要注意的是,传入的InvocationHandler是this,说明JdkDynamicAopProxy也是一个InvocationHandler,即继承了InvocationHandler,那么就必然要实现代理逻辑的invoke()方法。到这我们就明白了,真正的代理逻辑肯定封装在JdkDynamicAopProxy#invoke方法中。

JdkDynamicAopProxy#invoke源码如下所示:

// JdkDynamicAopProxy.java
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // [4.1详细介绍] 预定义变量,注意到this.advised这个全局变量,它其实就是ProxyFactory
    Object oldProxy = null;
    boolean setProxyContext = false;
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // 处理一些非增强的方法,不是重点,可以忽略
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            return hashCode();
        } else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
              method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
  
        Object retVal;
  
        if (this.advised.exposeProxy) {
            // [4.2详细介绍] 将代理暴露到当前ThreadLocal中
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
  
        
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
  
        // [4.3详细介绍] 获取拦截链,这个拦截链由所有advisor组成
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
        if (chain.isEmpty()) {
            // [4.4详细介绍] 如果拦截链为空,说明用户并未配置增强(advise或者advisor),此时直接调用AopUtils.invokeJoinpointUsingReflection()方法反射执行原方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        } else {
            // [4.5详细介绍] 拦截链不为空,首先执行拦截链,类似于责任链的执行
            MethodInvocation invocation =
                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            retVal = invocation.proceed();
        }
  
        // 如果需要,处理返回值的类型
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
              returnType != Object.class && returnType.isInstance(proxy) &&
              !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            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()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // 将原来threadlocal中的对象设置回去。
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

invoke()方法看上去很多,但其实主流程其实非常易懂。就是从用户配置的advice、advisor中创建拦截链(每一个advisor都是一个拦截点),然后依照责任链模式执行下去,直到执行待增强的方法。

大家有了主流程的执行概念后,下面对其中重要的部分进行深入分析。

4.1、this.advised变量介绍

预定义变量中的this.advised值得我们注意,他其实就是我们最开始创建的ProxyFactory,大家可以返回去看下3.3小节,在构造JdkDynamicAopProxy时,我们将this作为config传入到JdkDynamicAopProxy,JdkDynamicAopProxy使用的就是advised变量进行接收。因此,this.advised===ProxyFactory。这里面包含了用户配置的advisor列表(即pointcut和advice),后期用户配置拦截链也要强依赖this.advised

4.2、暴露代理到ThreadLocal

我们来看下setCurrentProxy()这个方法,将代理类暴露到当前线程的ThreadLocal中。

为什么要把这个单独抽出来介绍呢?不知道大家还记不记得在声明式事务中,如果一个类的非事务方法调用了本类的事务方法,该事务并不会生效。此时我们可以在非事务方法中使用AopContext.currentProxy()获取当前的类代理,然后再用该代理调用事务方法即可生效。而AopContext.currentProxy()获取的代理类则是在此时set到threadlocal中的。

// AopContext.java
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
@Nullable
static Object setCurrentProxy(@Nullable Object proxy) {
    Object old = currentProxy.get();
    if (proxy != null) {
        currentProxy.set(proxy);
    }
    else {
        currentProxy.remove();
    }
    return old;
}

4.3、拦截链的构造

拦截链构造的代码如下所示,构造时主要还是依赖this.advised这个变量。

// JdkDynamicAopProxy.java
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

我们进入到方法内部,这里主要对缓存进行操作,因为构造拦截链比较耗时,使用缓存能够大幅度提高效率。

这里多嘴一句,记得上面我们添加advice或advisor时会清空缓存嘛?如果想要固定整个拦截链,提高cache hit比例,我们可以在配置完advice和advisor后frozen ProxyFactory,禁止用户配置其他增强,保证缓存不被清空。

// AdvisedSupport.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    // 将method封装成MethodCacheKey
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    // 尝试从缓存中获取,如果缓存存在直接返回
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        // 缓存中不存在,构造拦截链
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
            this, method, targetClass);
        // 添加进缓存
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}

真正的构造拦截链的方法还是在advisorChainFactory中,advisorChainFactory只有一个默认的实现类,DefaultAdvisorChainFactory,我们进入该类的方法里面看看。

// DefaultAdvisorChainFactory.java
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
      Advised config, Method method, @Nullable Class<?> targetClass) {

    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    // 获取advisors列表
    Advisor[] advisors = config.getAdvisors();
    // 用于存储拦截链中的节点
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;
    
    // 遍历advisors列表
    for (Advisor advisor : advisors) {
        // 判断advisor的类型,之前说过,大部分advisor都是DefaultPointcutAdvisor,因此我们主要分析PointcutAdvisor的情况
        if (advisor instanceof PointcutAdvisor) {
          // 强转一下
          PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
          // 首先使用advisor中pointcut的ClassFilter判断一下增强的class是否能够匹配的上
          if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
              // 能够匹配,再获取pointcut中MethodMatcher,判断增强的方法是否能够匹配
              MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
              boolean match;
              if (mm instanceof IntroductionAwareMethodMatcher) {
                  if (hasIntroductions == null) {
                      hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                  }
                  match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
              } else {
                  match = mm.matches(method, actualClass);
              }
              // 如果方法也能够匹配上,说明已经找到了切点,此时可以获取方法的增强。这里将advisor中的adivce装换成MethodInterceptor,然后全部添加进interceptorList再返回。
              if (match) {
                  MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                  if (mm.isRuntime()) {
                      for (MethodInterceptor interceptor : interceptors) {
                          interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                      }
                  }
                  else {
                      interceptorList.addAll(Arrays.asList(interceptors));
                  }
              }
          }
      } else if (advisor instanceof IntroductionAdvisor) {
          // ...
      } else {
          // ...
      }
    }
    // 返回构造好的拦截链
    return interceptorList;
}

这个方法的详细作用已经在每行代码的注释上说明出来了,其实主要就执行了两个逻辑:

  • 通过pointcut判断class和method是否能够匹配
  • 如果能够匹配,将advisor和MethodMatcher包装成InterceptorAndDynamicMethodMatcher,然后添加进拦截列表中并返回

pointcut匹配的过程大家感兴趣可以去深入了解下,我们这里介绍下advisor的包装过程。

// 这里的registry实现类为 DefaultAdvisorAdapterRegistry
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

// DefaultAdvisorAdapterRegistry.java
private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);

// 预置adapter
public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
    Advice advice = advisor.getAdvice();
    // [1] 如果 advice 属于 MethodInterceptor,强转一下直接添加即可
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    // [2] 如果 advice 不属于 MethodInterceptor,使用 AdviceAdapter 对 advice 进行包装,包装成 MethodInterceptor
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
  • [1] 如果我们当初配置的advice已经继承了MethodInterceptor的话(类似MethodBeforeAdviceInterceptor),直接强转添加进列表即可
  • [2] 如果未继承,会通过提前预置的adapter对advice进行转化,转化过程也非常简单,就是通过构造器进行包装:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {
        // 判断是否支持其实就是判断是否继承某个父类
        return (advice instanceof MethodBeforeAdvice);
    }
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        // 如果继承,则进行包装
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}

至此,拦截链已经完成构造了。

4.4、反射执行原方法

上面介绍了拦截链的构造。但大家可以想一想,如果我们配置的pointcut并未匹配任何一个类或者方法,拦截链最终为空,那岂不是任何增强都没有?Spring也帮我们想到了这一点,在没有任何增强的场景下,会调用AopUtils#invokeJoinpointUsingReflection方法直接反射执行对应的方法。

// JdkDynamicAopProxy.java
if (chain.isEmpty()) {
    // 如果拦截链为空,首先获取方法的对应的参数
    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    // 然后直接反射执行
    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}

AopUtils#invokeJoinpointUsingReflection源码如下所示:

// AopUtils.java
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
        throws Throwable {
  try {
      // 
      ReflectionUtils.makeAccessible(method);
      return method.invoke(target, args);
  } // ...省略掉一大堆catch

4.5、拦截链的执行

如果拦截链不为空,则开始执行具体增强过程。

// JdkDynamicAopProxy.java
MethodInvocation invocation =
    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();

// ReflectiveMethodInvocation.java
@Override
@Nullable
public Object proceed() throws Throwable {
    // [1] 如果当前执行拦截点的索引为最后一个,说明之前所有的拦截点已经执行完成,也即所有的增强过程已经执行完成,此时需要执行连接点、即被增强的方法,invokeJoinpoint也是调用AopUtils#invokeJoinpointUsingReflection反射执行被增强的方法。
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // [2] 如果索引不是最后一个,获取拦截链指定索引处的增强InterceptorAndDynamicMethodMatcher,并自增索引
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 判断方法和类是否匹配
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            // 如果匹配的话,则执行InterceptorAndDynamicMethodMatcher中methodInterceptor中的invoke方法,注意这次将this(ReflectiveMethodInvocation)传递进了invoke方法中,下面我们将用MethodBeforeAdviceInterceptor举例分析invoke方法。
            return dm.interceptor.invoke(this);
        } else {
            // 递归调用本方法
            return proceed();
        }
    } else {
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
}

// MethodBeforeAdviceInterceptor.java
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    // 首先执行增强逻辑
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    // 这里的mi是ReflectiveMethodInvocation,执行proceed其实就是执行上面的proceed方法,上面的proceed方法便会继续从拦截链中获取下一个增强继续执行。
    return mi.proceed();
}

执行增强过程的逻辑注释中已经分析的非常详细了,这里有几个小点补充下:

  • [1] 这里一定是正常执行增强链的最后一步,即执行被增强(连接点)方法的地方,其实这么做也非常合理。对于拦截链来说,总得执行完前面的增强方法才能执行被增强方法啊,不然配置这些增强的意义又在哪呢。但是,对于不同的methodInterceptor来说,增强点会由自己控制,比如AfterReturningAdviceInterceptor,是先执行proceed方法,即执行到增强链的最后一步,获取被增强方法的返回值后再执行增强。
  • [2] 这里获取的是InterceptorAndDynamicMethodMatcher,这是因为我们之前在构造拦截链的时候,将最终的matcher和MethodInterceptor封装成了InterceptorAndDynamicMethodMatcher。因此,拦截链上的每一个拦截点其实都是InterceptorAndDynamicMethodMatcher。

拦截链执行完成后,AOP(基于JDK动态代理)的执行也就差不多完成了,后面就是一些处理返回结果的逻辑,大家感兴趣可以自行了解。

4.6、小结

这里我们总结下JdkDynamicAopProxy获取代理的过程。

SpringAOP基于JDK动态代理获取代理类最终是由JdkDynamicAopProxy实现的。JdkDynamicAopProxy里封装了getProxy()方法获取目标类的代理类proxy,而每次执行代理类的方法时,都会被JdkDynamicAopProxy#invoke进行拦截(这里是基本的JDK动态代理内容,不了解的可以网上自行google)。

在invoke方法中,会从缓存中获取执行方法对应的拦截链,如果不存在则会立即构建拦截链并存放进缓存中。拦截链其实就是一个InterceptorAndDynamicMethodMatcher列表。每个InterceptorAndDynamicMethodMatcher都封装由MethodMatcher和MethodInterceptor,前者用于判断当前执行方法是否需要拦截,后者则是具体执行的拦截内容。而在该拦截链的末尾处则是被代理类的方法。

执行拦截链的过程就是遍历拦截列表,判断是否需要拦截增强,直至执行到末尾。