Spring AOP源码学习:一次请求调用全流程

简介: 在上篇文章中,我们介绍了 AOP 代理的创建:Spring AOP源码学习:创建 AOP 代理,本文将介绍最后的一个重要内容:使用了 AOP 代理的方法的一次完整调用流程。

目录

前言

正文

JdkDynamicAopProxy#invoke

代码块1ReflectiveMethodInvocation 构造函数

代码块2ReflectiveMethodInvocation#proceed()

代码块3invokeJoinpoint()

代码块4ExposeInvocationInterceptor#invoke

代码块5AspectJAroundAdvice#invoke

代码块6invokeAdviceMethod

代码块7argBinding

代码块8invokeAdviceMethodWithGivenArgs

总结

相关文章


前言


在上篇文章中,我们介绍了AOP代理的创建:Spring AOP源码学习:创建 AOP 代理,本文将介绍最后的一个重要内容:使用了 AOP 代理的方法的一次完整调用流程。

 

正文


关于使用的 AOP 增强方式,本文以最常使用的 @Around 作为例子进行介绍,如下代码。

@Aspect
@Component
public class LogInterceptor {
    @Pointcut("execution(* com.joonwhee.open.service..*.*(..))")
    public void pointcut() {
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        try {
            System.out.println("入参=" + JSON.toJSONString(args));
            long start = System.currentTimeMillis();
            Object result = pjp.proceed();
            System.out.println("耗时=" + (System.currentTimeMillis() - start));
            System.out.println("回参=" + JSON.toJSONString(result));
            return result;
        } catch (Exception e) {
            return "";
        }
    }
}

关于被代理的方法,使用下面的 getName(String name) 作为例子进行介绍,如下代码。

@Service
public class UserServiceImpl implements UserService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
    @Override
    public String getName(String name) {
        LOGGER.info("UserServiceImpl#getName入参, name={}", name);
        String result = "name: " + name;
        LOGGER.info("UserServiceImpl#getName回参, result={}", result);
        return result;
    }
}

 

当我们调用了被 AOP 代理的方法时,使用 JDK 动态代理会走到 JdkDynamicAopProxy#invoke 方法,使用 CBLIB 代理会走到DynamicAdvisedInterceptor#intercept 方法,两者的内容基本一样,这里就拿更常见的 JdkDynamicAopProxy#invoke 方法来介绍。

 

JdkDynamicAopProxy#invoke

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;
    // 1.advised就是proxyFactory,而targetSource持有被代理对象的引用
    TargetSource targetSource = this.advised.targetSource;
    Class<?> targetClass = null;
    Object target = null;
    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            // 目标不实现equals(Object)方法本身。
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        } else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            // 只有getDecoratedClass()声明 - > dispatch到代理配置。
            return AopProxyUtils.ultimateTargetClass(this.advised);
        } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            // ProxyConfig上的服务调用与代理配置...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        Object retVal;
        // 有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // 2.拿到我们被代理的对象实例
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        // Get the interception chain for this method.
        // 3.获取拦截器链:例如使用@Around注解时会找到AspectJAroundAdvice,还有ExposeInvocationInterceptor
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        // 4.检查我们是否有任何拦截器(advice)。 如果没有,直接反射调用目标,并避免创建MethodInvocation。
        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            // 5.不存在拦截器链,则直接进行反射调用
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        } else {
            // We need to create a method invocation...
            // 6.如果存在拦截器,则创建一个ReflectiveMethodInvocation:代理对象、被代理对象、方法、参数、
            // 被代理对象的Class、拦截器链作为参数创建ReflectiveMethodInvocation
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            // 7.触发ReflectiveMethodInvocation的执行方法
            retVal = invocation.proceed();
        }
        // Massage return value if necessary.
        // 8.必要时转换返回值
        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);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

3.获取拦截器链:使用 @Around 注解时会找到 AspectJAroundAdvice,还有 ExposeInvocationInterceptor,其中ExposeInvocationInterceptor 在前,AspectJAroundAdvice 在后。


6.如果存在拦截器,则创建一个 ReflectiveMethodInvocation,代理对象、被代理对象、方法、参数、被代理对象的 Class、拦截器链作为参数。这边 ReflectiveMethodInvocation 已经持有了被代理对象、方法、参数,后续就可以直接使用反射来调用被代理的方法了,见代码块1


7.触发 ReflectiveMethodInvocation 的执行方法,见代码块2

 

代码块1ReflectiveMethodInvocation 构造函数

protected ReflectiveMethodInvocation(
        Object proxy, Object target, Method method, Object[] arguments,
        Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
    this.proxy = proxy;
    this.target = target;
    this.targetClass = targetClass;
    this.method = BridgeMethodResolver.findBridgedMethod(method);
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

代码块2ReflectiveMethodInvocation#proceed()

@Override
public Object proceed() throws Throwable {
    //  We start with an index of -1 and increment early.
    // 1.如果所有拦截器都执行完毕(index是从-1开始,所以跟size - 1比较),则直接使用反射调用连接点(也就是我们原本的方法)
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // 2.每次调用时,将索引的值递增,并通过索引拿到要执行的拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 3.判断拦截器是否为InterceptorAndDynamicMethodMatcher类型(动态方法匹配拦截器)
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        // 进行动态匹配。在此评估动态方法匹配器:静态部件已经过评估并且发现匹配。
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        } else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            // 动态匹配失败。跳过此拦截器并调用链中的下一个。
            return proceed();
        }
    } else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // 4.只是一个普通的拦截器,则触发拦截器链责任链的调用,并且参数为ReflectiveMethodInvocation本身
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

该方法是一个责任链的方法,会按索引执行所有的拦截器。

1.如果所有拦截器都执行完毕(index是从-1开始,所以跟size - 1比较),则直接使用反射调用连接点(也就是我们原本的方法),见代码块3

4.只是一个普通的拦截器,则直接调用它,参数为自己本身,在本文的例子,interceptorsAndDynamicMethodMatchers 有两个拦截器:ExposeInvocationInterceptor 在前,AspectJAroundAdvice 在后,因此首先会触发 ExposeInvocationInterceptor invoke 方法,见代码块4

 

代码块3invokeJoinpoint()

protected Object invokeJoinpoint() throws Throwable {
    // 反射执行连接点,也就是原方法,target为被代理的对象实例、method为执行的方法、arguments为方法参数
    return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
public static Object invokeJoinpointUsingReflection(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);
    }
}

 

代码块4ExposeInvocationInterceptor#invoke

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation oldInvocation = invocation.get();
    // 1.设置为当前的MethodInvocation
    invocation.set(mi);
    try {
        // 2.继续进入链中的下一个拦截器。
        return mi.proceed();
    } finally {
        // 3.执行结束设置回原来的MethodInvocation
        invocation.set(oldInvocation);
    }
}

2.继续进入链中的下一个拦截器,该方法会回到代码块1,从而拿到下一个拦截器,并触发其 invoke 方法,在本例中也就是:AspectJAroundAdvice#invoke,见代码块5

 

代码块5AspectJAroundAdvice#invoke

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    // 1.这边的mi就是我们的ReflectiveMethodInvocation,
    // ReflectiveMethodInvocation实现了ProxyMethodInvocation接口,所以这边肯定通过校验
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    // 2.将mi直接强转成ProxyMethodInvocation,mi持有代理类实例proxy、被代理类实例target、被代理的方法method等
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    // 3.将pmi封装层MethodInvocationProceedingJoinPoint(直接持有入参mi,也就是ReflectiveMethodInvocation的引用)
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    // 4.拿到pointcut的表达式
    JoinPointMatch jpm = getJoinPointMatch(pmi);
    // 5.调用增强方法
    return invokeAdviceMethod(pjp, jpm, null, null);
}

5.调用增强方法,见代码块6

 

代码块6invokeAdviceMethod

protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
        throws Throwable {
    // 1.argBinding:获取方法执行连接点处的参数
    // 2.invokeAdviceMethodWithGivenArgs:使用给定的参数调用增强方法
    return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}

1.获取方法执行连接点处的参数,见代码块7

2.使用 argBinding 方法返回的参数调用增强方法,在本文给出的例子中,也就是 LogInterceptor中被 @Around 修饰的 around(ProceedingJoinPoint pjp) 方法,见代码块8

 

代码块7argBinding

protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
    calculateArgumentBindings();
    // AMC start
    Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
    int numBound = 0;
    if (this.joinPointArgumentIndex != -1) {
        // 1.如果存在连接点参数,则将jp添加到调用参数
        // 当使用@Around时就有参数;使用@Before、@After时就没有参数
        adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
        numBound++;
    } else if (this.joinPointStaticPartArgumentIndex != -1) {
        adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
        numBound++;
    }
    if (!CollectionUtils.isEmpty(this.argumentBindings)) {
        // binding from pointcut match
        // 2.使用pointcut匹配绑定
        if (jpMatch != null) {
            PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
            for (PointcutParameter parameter : parameterBindings) {
                String name = parameter.getName();
                Integer index = this.argumentBindings.get(name);
                adviceInvocationArgs[index] = parameter.getBinding();
                numBound++;
            }
        }
        // binding from returning clause
        // 3.用于绑定@AfterReturing中的returning参数
        if (this.returningName != null) {
            Integer index = this.argumentBindings.get(this.returningName);
            adviceInvocationArgs[index] = returnValue;
            numBound++;
        }
        // binding from thrown exception
        // 4.用于绑定@AfterThrowing中的throwing参数
        if (this.throwingName != null) {
            Integer index = this.argumentBindings.get(this.throwingName);
            adviceInvocationArgs[index] = ex;
            numBound++;
        }
    }
    if (numBound != this.parameterTypes.length) {
        throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
                " arguments, but only bound " + numBound + " (JoinPointMatch " +
                (jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
    }
    return adviceInvocationArgs;
}

1.如果存在连接点参数,则将 jp 添加到增强方法的参数数组,对于 @Around 来说,这边的 jp 就是代码块5中的入参 mi,也就是我们之前创建的 ReflectiveMethodInvocation 对象。所以,当使用 @Around 时,这边返回的增强方法的参数数组持有的是 ReflectiveMethodInvocation 对象。

 

代码块8invokeAdviceMethodWithGivenArgs

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    // 1.如果增强方法没有参数,则将actualArgs赋值为null
    if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // TODO AopUtils.invokeJoinpointUsingReflection
        // 2.反射执行增强方法
        return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    } catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
                this.aspectJAdviceMethod + "]; pointcut expression [" +
                this.pointcut.getPointcutExpression() + "]", ex);
    } catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }
}

2.反射执行增强方法,对于本文给出的例子,这边会直接走到 LogInterceptor 中被 @Around 修饰的 around(ProceedingJoinPoint pjp) 方法,该方法会执行一些增强逻辑,最终执行 “Object result = pjp.proceed()”


通过代码块6和代码块7我们知道,这边的 pjp 就是我们之前创建的 ReflectiveMethodInvocation 对象,所以这边会再次调用 ReflectiveMethodInvocation 对象的 process() 方法,也就是回到代码块2。此时我们的拦截器都已经执行完毕,因此会走到 invokeJoinpoint() 方法,通过反射执行我们被代理的方法,也就是 getName(String name) 方法。


至此,AOP 的一次调用流程就全部走通了。

 

总结


AspectJ 方式的 AOP 内容到此就介绍完毕了,核心流程如下。


1)解析 AOP 的注解,并注册对应的内部管理的自动代理创建者的 bean,对于本次介绍是:AnnotationAwareAspectJAutoProxyCreator,其他的还有

InfrastructureAdvisorAutoProxyCreator AspectJAwareAdvisorAutoProxyCreator

2)当我们的 bean 初始化完毕后,会触发所

BeanPostProcessor postProcessAfterInitialization 方法,此时就会调用我们的 AnnotationAwareAspectJAutoProxyCreator postProcessAfterInitialization 方法。该方法会查找我们定义的切面类(使用 @Aspect 注解),创建切面类中定义的增强器(使用 @Before@After@Around 等注解),并根据 @Pointcut execution 表达式筛选出适用于当前遍历的 bean 的增强器, 将适用于当前遍历的 bean 的增强器作为参数之一创建对应的 AOP 代理。

3)当调用到被 AOP 代理的方法时,会走到对应的代理方法:JdkDynamicAopProxy#invoke  DynamicAdvisedInterceptor#intercept,该方法会创建 ReflectiveMethodInvocation,通过责任链的方式来执行所有的增强器和被代理的方法。

 

相关文章


Spring AOP源码学习:基本概念

Spring AOP源码学习:AOP 注解的解析

Spring AOP源码学习:创建 AOP 代理

相关文章
|
3天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
9 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
6天前
|
设计模式 前端开发 Java
Spring MVC——项目创建和建立请求连接
MVC是一种软件架构设计模式,将应用分为模型、视图和控制器三部分。Spring MVC是基于MVC模式的Web框架,通过`@RequestMapping`等注解实现URL路由映射,支持GET和POST请求,并可传递参数。创建Spring MVC项目与Spring Boot类似,使用`@RestController`注解标记控制器类。
18 1
Spring MVC——项目创建和建立请求连接
|
4天前
|
JavaScript Java 关系型数据库
自主版权的Java诊所管理系统源码,采用Vue 2、Spring Boot等技术栈,支持二次开发
这是一个自主版权的Java诊所管理系统源码,支持二次开发。采用Vue 2、Spring Boot等技术栈,涵盖患者管理、医生管理、门诊管理、药店管理、药品管理、收费管理、医保管理、报表统计及病历电子化等功能模块。
|
9天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
9天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
13天前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
28 2
|
15天前
|
前端开发 Java Spring
【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart
【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart
20 2
|
15天前
|
JSON 前端开发 Java
【Spring】“请求“ 之传递 JSON 数据
【Spring】“请求“ 之传递 JSON 数据
52 2
|
15天前
|
前端开发 Java Spring
【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
60 2
|
10天前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
24 0