spring aop源码解析

2017-07-23 10:54:59

这篇文章主要叙述spring aop内部源码的实现,不当之处,还望指出

一个小栗子

定义一个服务接口

package com.spring.start.service;

public interface IBlogService {
    int getBlogCount();
}

接口实现类

package com.spring.start.service;

public class BlogService implements IBlogService {
    @Override
    public int getBlogCount() {
        int count = 123;
        System.out.println("blog count : " + count);
        return count;
    }
}

定义一个切面(Aspect)

@Aspect
public class LogAspect {

    @Around("execution(public * com.spring.start..*.*.*(..))")
    public Object methodAroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("log1 : start method");
        Object result = joinPoint.proceed(joinPoint.getArgs());
        System.out.println("log2 : end method");
        return result;
    }


    @Around("execution(public * com.spring.start..*.*.*(..))")
    public Object methodAroundLog2(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("log2 : start method");
        Object result = joinPoint.proceed(joinPoint.getArgs());
        System.out.println("log2 : end method");
        return result;
    }
}

切面类中定义了两个通知(Advice)methodAroundLog和methodAroundLog2,对com.spring.start下所有子包中所有类的所有pubic方法进行拦截,并输出写简单的log信息。

来看看配置文件application.xml

    <!-- 开启aop -->
    <aop:aspectj-autoproxy/>

    <!-- 定义切面 -->
    <bean id="logAspect" class="com.spring.start.aop.LogAspect"></bean>

    <!-- 定义service -->
    <bean id="blogService" class="com.spring.start.service.BlogService"></bean>

测试方法

public static void main(String[] args) {
    ApplicationContext beanFactory = new ClassPathXmlApplicationContext("application.xml");
    IBlogService blogService = beanFactory.getBean("blogService", IBlogService.class);
    blogService.getBlogCount();        
}

输出结果:

log1 : start method
log2 : start method
blog count : 123
log2 : end method
log2 : end method

可以看到aop已成功拦截到了方法,并输出了对应的log。

一开始,先明确几个概念
aspect:切面,该切面可以包含多个切入点和通知,而且标签内部的通知和切入点定义是无序的
advice:通知,表示一个method执行前或执行后的动作。上面栗子中定义了两个环绕通知
pointcut:切入点,表示根据method的名字或者正则表达式去拦截一个method。
advisor:只有一个通知和一个切入点的切面,可以看做一种特殊的aspect

开始阅读源码前,我比较关注的几个问题:

  1. <aop:aspectj-autoproxy/>的作用
  2. spring如何根据Aspect注解创建切面
  3. spring如何根据切面信息创建代理对象
  4. 上面定义的两个通知methodAroundLog和methodAroundLog2,spring是如何进行链式调用的

<aop:aspectj-autoproxy/> 的作用

aspectj-autoproxy是spring自定义的标签。我们知道,在spring中自定义标签,需要编写一个继承于NamespaceHandlerSupport的类来实现标签解析工作。
在github spring源码中搜索一下,就可以发现aspectj-autoproxy的解析类是AopNamespaceHandler:

public void init() {
    this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}

而AspectJAutoProxyBeanDefinitionParser类负责对aspectj-autoproxy标签进行解析:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    extendBeanDefinition(element, parserContext);
    return null;
}

关键代码是AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {

    BeanDefinition beanDefinition = 
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    registerComponentIfNecessary(beanDefinition, parserContext);
}

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary:

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

这里可以看到一个关键的类AnnotationAwareAspectJAutoProxyCreator,该类负责创建切面,registerOrEscalateApcAsRequired就是把该类注入到spring上下文环境中,以便后面使用。

private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    ...
    // 创建RootBeanDefinition, RootBeanDefinition是spring的元素单元
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);    
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 注册到spring上下文
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);        
    return beanDefinition;
}

创建切面

spring中提供了BeanPostProcessor扩展接口,该接口有两个方法postProcessBeforeInitialization和postProcessAfterInitialization,实现该接口这两个方法,就可以在spring完成Bean的实例化前后添加自己的逻辑处理。

上面被aspectj-autoproxy解析类注入到spring上下文的AnnotationAwareAspectJAutoProxyCreator类,继承自AbstractAutoProxyCreator类,并实现了BeanPostProcessor。
来看看AbstractAutoProxyCreator.postProcessAfterInitialization(spring会在目标bean生成后调用该方法)

public Object postProcessAfterInitialization(Object bean, String beanName) ... {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

主要操作在wrapIfNecessary:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ...

    // 创建Advices和Advisors
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理
        Object proxy = createProxy(bean.getClass(), 
                beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

getAdvicesAndAdvisorsForBean会创建通知,该方法会调用子类AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean,并调用的AbstractAdvisorAutoProxyCreator.findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 创建Advisors
    List<Advisor> candidateAdvisors = findCandidateAdvisors();    
    // 过滤Advisors
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);    
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

这是一个非常重要的方法,实现了几个关键步骤

  1. findCandidateAdvisors 查找Advisors(第一次会创建)
  2. findAdvisorsThatCanApply 根据目标bean的class过滤一部分的Advisors
  3. extendAdvisors 扩充Advisors,spring会根据需要添加一些内部的Advisors
  4. sortAdvisors 对Advisors排序

findCandidateAdvisors 查找Advisors

findCandidateAdvisors方法负责找到所有的Advisors,该方法会调用到AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    return advisors;
}

this.aspectJAdvisorsBuilder.buildAspectJAdvisors()是创建Advisor的关键,来看看BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;

    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {


                List<Advisor> advisors = new LinkedList<Advisor>();
                aspectNames = new LinkedList<String>();
                // 获取所以的bean
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);

                for (String beanName : beanNames) {
                    ...
                    // 如果有Aspect注解
                    if (this.advisorFactory.isAspect(beanType)) {    
                        ...
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            // 创建factory
                            MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);    
                            // 创建Advisors
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);        
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);    
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        }
                    }    
                }
            }
        }    
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    // 构建结果
    List<Advisor> advisors = new LinkedList<Advisor>();
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

这里可以看到一个简单的单例模式,如果当前的aspectBeanNames为null,会创建Advisors,并保存在advisorsCache中,以后每次都从advisorsCache取出advisors。

可以看到,如果一个类有Aspect注解,就会使用该类的beanName创建MetadataAwareAspectInstanceFactory,并通过factory创建Advisors
this.advisorFactory.getAdvisors(factory)将会调用到ReflectiveAspectJAdvisorFactory.getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 切面类
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();    
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);

    // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
    // so that it will only instantiate once.
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new LinkedList<Advisor>();
    // getAdvisorMethods会获取没有Pointcut注解的方法
    for (Method method : getAdvisorMethods(aspectClass)) {    
        // 创建Advisor
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);    
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

}    

//  创建Advisor
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {
    // 获取切入点Pointcut    
    AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());        
    if (expressionPointcut == null) {
        return null;
    }

    // 创建Advisor,Advisor中包含了pointcut和adviceMethod
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}    

//  获取切入点Pointcut
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 找到有Before, Around, After, AfterReturning, AfterThrowing, Pointcut注释的方法
    AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);        
    if (aspectJAnnotation == null) {
        return null;
    }

    // 生成AspectJExpressionPointcut,该类记录了Aspect表达式
    AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    ajexp.setBeanFactory(this.beanFactory);
    return ajexp;
}

从上面可以看到,对应Aspect注解了的类,spring会查找其中有Before, Around, After, AfterReturning, AfterThrowing, Pointcut的方法,并创建Advisor。

findAdvisorsThatCanApply 过滤Advisors

findAdvisorsThatCanApply方法,会过滤部分的Advisors
这里会检查目标bean的class和class中是否有方法可以匹配Advisor,如果没有则过滤。比较繁琐,不展开了

extendAdvisors(eligibleAdvisors) 扩充Advisors

看看AspectJAwareAdvisorAutoProxyCreator.extendAdvisors

protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // Don't add advisors to an empty list; may indicate that proxying is just not required
    if (!advisors.isEmpty()) {
        ...
        if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            // 添加ExposeInvocationInterceptor
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true;
        }
    }
    return false;
}

spring在advisors的开始位置添加了ExposeInvocationInterceptor

创建代理

我们知道,spring通过动态代理类实现aop,有jdk动态代理和cglib两种方法。
如果要使用jdk动态代理,被代理类必须实现一个接口。
为了避免在这里额外介绍cglib,我在上面的小栗子中让spring生成的BlogService实现了一个接口,这样我们来看一下jdk动态代理如何实现aop

回顾AbstractAutoProxyCreator.wrapIfNecessary方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 构建Advisors
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 生成代理
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
}

getAdvicesAndAdvisorsForBean已经讲解(getAdvicesAndAdvisorsForBean已经查找了Advices和Advisors,buildAdvisors只是做一些检查,转换处理,如将Advices转换成Advisor),
这里来看看动态代理创建过程AbstractAutoProxyCreator.createProxy

protected Object createProxy(
    ...
    Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 存储Advisor
    for (Advisor advisor : advisors) {    
        proxyFactory.addAdvisor(advisor);
    }

    return proxyFactory.getProxy(getProxyClassLoader());
}

ProxyFactory.getProxy

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

调用父类ProxyCreatorSupport.createAopProxy

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    return getAopProxyFactory().createAopProxy(this);
}

getAopProxyFactory().createAopProxy(this)中参数是this,proxyFactory将自身作为参数(proxyFactory存储着Advisor),调用DefaultAopProxyFactory.createAopProxy

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        ...
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

可以看到,spring会根据用户配置和目标bean是否有实现接口,来决定使用JdkDynamicAopProxy或ObjenesisCglibAopProxy

来看看JdkDynamicAopProxy.getProxy

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

注意:Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)第三个参数是JdkDynamicAopProxy.this,JdkDynamicAopProxy实现了InvocationHandler

链式调用

JdkDynamicAopProxy实现了InvocationHandler,看一下关键的invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 获取拦截器链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // Proceed to the joinpoint through the interceptor chain.
    retVal = invocation.proceed();
}

this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass) 获取拦截器链,

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
    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;
}

DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass) {

    ...
    for (Advisor advisor : config.getAdvisors()) {
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // 检查class是否匹配
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {    
                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                // 检查method 是否匹配
                if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {    
                    if (mm.isRuntime()) {
                        // Creating a new object instance in the getInterceptors() method
                        // isn't a problem as we normally cache created chains.
                        for (MethodInterceptor interceptor : interceptors) {    
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }
    }
}

可以看到,这里从Advisor获取到对应的MethodInterceptor数组,MethodInterceptor中包括了Advisor中定义的advice(通知)。

回到ReflectiveMethodInvocation.proceed,ReflectiveMethodInvocation,顾名思义,反射方法进行调用

public Object proceed() throws Throwable {
    // 是否到拦截器链尾了
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {    
        return invokeJoinpoint();
    }

    // 获取下一个拦截器,currentInterceptorIndex初始值是-1
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);    
    // 是否为动态拦截器
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {    
        ...
    }
    else {
        // 直接调用
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);    
    }
}

注意,invoke会将ReflectiveMethodInvocation自身作为参数。

调用链以ExposeInvocationInterceptor开头,ExposeInvocationInterceptor只是简单的配置和恢复上下文

public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation oldInvocation = invocation.get();
    // 设置上下文
    invocation.set(mi);    

    try {
        return mi.proceed();
    }
    finally {
        invocation.set(oldInvocation);    // 恢复上下文
    }
}

mi.proceed()会调用到ReflectiveMethodInvocation.proceed(),会调用到下一个拦截器,这时进入AspectJAroundAdvice

public Object invoke(MethodInvocation mi) throws Throwable {
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    JoinPointMatch jpm = getJoinPointMatch(pmi);
    return invokeAdviceMethod(pjp, jpm, null, null);
}

protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
        throws Throwable {
    // argBinding绑定参数
    return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));    
}

argBinding负责绑定参数,实际就是把JoinPoint作为第一个方法参数。

protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
    ...
    int numBound = 0;

    if (this.joinPointArgumentIndex != -1) {
        adviceInvocationArgs[this.joinPointArgumentIndex] = jp;

    }

    return adviceInvocationArgs;
}

AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs会调用到AdviceMethod(aspectJAdviceMethod),就是执行通知

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}

在环绕通知中

    @Around("...")
    public Object methodAroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed(joinPoint.getArgs());
        return result;
    }

由于第一个参数就是JoinPoint,所以joinPoint.proceed(joinPoint.getArgs());`就可以调用下一个拦截器,这样就实现了拦截器链调用