Spring系列之AOP分析之对通知方法的执行过程(九)

简介:

我们在上一篇文章中说到了前置通知的方法调用AspectJMethodBeforeAdvice#before,在这个before方法中又调用了invokeAdviceMethod这个方法,invokeAdviceMethod这个方法在AspectJMethodBeforeAdvice的父类AbstractAspectJAdvice中。AbstractAspectJAdvice这个是Aspect的所有通知类型的共同父类。关于AbstractAspectJAdvice中的invokeAdviceMethod方法,有两个重载的方法。前置通知、后置通知、异常通知、后置返回通知都是用的AbstractAspectJAdvice#invokeAdviceMethod(org.aspectj.weaver.tools.JoinPointMatch, java.lang.Object, java.lang.Throwable)这个方法,环绕通知用的是:AbstractAspectJAdvice#invokeAdviceMethod(org.aspectj.lang.JoinPoint, org.aspectj.weaver.tools.JoinPointMatch, java.lang.Object, java.lang.Throwable)这个方法。这两个重载方法的区别是:后置通知调用的方法多了一个JoinPoint的参数。
invokeAdviceMethod方法的源码如下:

    //这三个参数 JoinPointMatch 都是相同的
    //returnValue 当执行后置返回通知的时候 传值 其他为null
    //Throwable  当执行后置异常通知的时候 传值,其他为null
    protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
        return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
    }
    //重载的方法 这个 JoinPoint 是ProceedingJoinPoint
    protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
            throws Throwable {

        return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
    }

我们先看getJoinPoint这个方法,其源码如下:

    protected JoinPoint getJoinPoint() {
        return currentJoinPoint();
    }
    public static JoinPoint currentJoinPoint() {
        //这里就不用再多说了  获取当前的MethodInvocation 即ReflectiveMethodInvocation的实例
        MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
        if (!(mi instanceof ProxyMethodInvocation)) {
            throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
        }
        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
        //JOIN_POINT_KEY 的值 为 JoinPoint.class.getName()
        //从ReflectiveMethodInvocation中获取JoinPoint 的值
        //这里在第一次获取的时候 获取到的 JoinPoint是null 
        //然后把下面创建的MethodInvocationProceedingJoinPoint放入到ReflectiveMethodInvocation的userAttributes中
        //这样在第二次获取的是 就会获取到这个 MethodInvocationProceedingJoinPoint
        JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
        if (jp == null) {
            jp = new MethodInvocationProceedingJoinPoint(pmi);
            pmi.setUserAttribute(JOIN_POINT_KEY, jp);
        }
        return jp;
    }

下面我们来看一下argBinding这个方法的作用和内容。从名字我们可以猜测这个方法的作用应该是进行参数绑定用的,我们来看一下:

    protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
        calculateArgumentBindings();

        // AMC start
        Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
        int numBound = 0;
        //这个默认值是 -1 重新赋值是在calculateArgumentBindings中进行的
        if (this.joinPointArgumentIndex != -1) {
            adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
            numBound++;
        }
        else if (this.joinPointStaticPartArgumentIndex != -1) {
            adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
            numBound++;
        }
        //这里主要是取通知方法中的参数类型 是除了 JoinPoint和ProceedingJoinPoint参数之外的参数
        //如异常通知参数 返回通知参数
        if (!CollectionUtils.isEmpty(this.argumentBindings)) {
            // binding from pointcut match
            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
            //后置返回通知参数
            if (this.returningName != null) {
                Integer index = this.argumentBindings.get(this.returningName);
                adviceInvocationArgs[index] = returnValue;
                numBound++;
            }
            // binding from thrown exception
            //异常通知参数
            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;
    }

calculateArgumentBindings

    public synchronized final void calculateArgumentBindings() {
        // The simple case... nothing to bind.
        //通知方法没有参数直接返回
        if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
            return;
        }
        
        int numUnboundArgs = this.parameterTypes.length;
        Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
        //从这里可以看出来我们的JoinPoint和ProceedingJoinPoint要放在通知方法的第一个参数
        if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
            numUnboundArgs--;
        }
        else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
            numUnboundArgs--;
        }
        //这里是对其他的参数的处理  处理过程还是比较复杂一点的 这里不再多说了。
        if (numUnboundArgs > 0) {
            // need to bind arguments by name as returned from the pointcut match
            bindArgumentsByName(numUnboundArgs);
        }
        this.argumentsIntrospected = true;
    }

这里还有再说一下AbstractAspectJAdvice这个类的构造函数,这个类只有这一个构造函数

    public AbstractAspectJAdvice(
            Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
        //通知方法不能为空
        Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
        //切面类
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
        //通知方法的名字
        this.methodName = aspectJAdviceMethod.getName();
        //通知方法参数
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
        //通知方法
        this.aspectJAdviceMethod = aspectJAdviceMethod;
        //切点类
        this.pointcut = pointcut;
        //切面实例的工厂类
        this.aspectInstanceFactory = aspectInstanceFactory;
    }

在创建通知类实例的时候,进行了上面的赋值的动作,把和通知相关的方法都传了进来。最后我们来看一下invokeAdviceMethodWithGivenArgs这个方法的内容:

    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
        Object[] actualArgs = args;
        //判断通知方法是否有参数
        if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
            actualArgs = null;
        }
        try {
            ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
            // TODO AopUtils.invokeJoinpointUsingReflection
            //反射调用通知方法
            //this.aspectInstanceFactory.getAspectInstance()获取的是切面的实例
            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();
        }
    }
相关文章
|
22天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
49 14
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
24天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
51 5
|
29天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
57 8
|
29天前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
29天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
41 5
|
29天前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
42 4
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
42 0
|
Java 应用服务中间件 数据库连接
Spring全家桶之Spring篇深度分析(一)
Spring 框架不局限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力。
Spring全家桶之Spring篇深度分析(一)
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
236 2