Spring系列之AOP分析之获取Advice的过程(四)

简介:

我们在前面的文章中分析了从切面类中获取Advisor的过程,我们最后创建的Advisor实例为:InstantiationModelAwarePointcutAdvisorImpl,它是一个Advisor和PointcutAdvisor的实现类,所以我们可以从这个类中获取Advice和Pointcut。从之前的分析中我们也看到了Pointcut的赋值,在这一篇文章中我们将会具体分析Advice的创建过程。
我们在上一篇文章的末尾说到了这一段代码可以实例化Advice。我们来看看这个方法的代码:

this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    //入参为切点表达式类
    //这里是通过调用aspectJAdvisorFactory来获取Advice
    //aspectJAdvisorFactory的实例是ReflectiveAspectJAdvisorFactory所以最终我们还是要到
    //ReflectiveAspectJAdvisorFactory中去分析Advice的获取过程
    //ReflectiveAspectJAdvisorFactory真是一个重要的类啊Advisor和Advice的获取都是在这个类中完成的
    //入参为:通知方法、切点表达式类、切面实例、切面的一个顺序、切面类名
    //倒腾了倒腾去基本上还是这几个参数
    return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    }

ReflectiveAspectJAdvisorFactory中getAdvice方法的代码如下

    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        //切面类 带有@Aspect注解的类
        Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        //验证切面类 不再分析了
        validate(candidateAspectClass);
        //又来一遍 根据通知方法 获取通知注解相关信息
        //在获取Advisor的方法 我们已经见过这个调用。这个在Spring的AnnotationUtils中会缓存方法的注解信息
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
        //再去校验一遍 如果不是切面类 则抛出异常
        if (!isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: " +
                    "Offending method '" + candidateAdviceMethod + "' in class [" +
                    candidateAspectClass.getName() + "]");
        }
        AbstractAspectJAdvice springAdvice;
        //在上一篇文章的分析中 我们说过在AspectJAnnotation中会存放通知类型
        switch (aspectJAnnotation.getAnnotationType()) {
            //如果是前置通知,则直接创建AspectJMethodBeforeAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //如果是后置通知,则直接创建AspectJAfterAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //如果是后置返回通知,则直接创建AspectJAfterReturningAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                //设置后置返回值的参数name
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
            //如果是后置异常通知,则直接创建AspectJAfterThrowingAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                //设置后置异常通知 异常类型参数name
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            //如果是后置异常通知,则直接创建AspectJAfterThrowingAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAround:
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //如果是切点方法,则什么也不做
            case AtPointcut:
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            //上面的那几种情况都不是的话,则抛出异常
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method: " + candidateAdviceMethod);
        }

        // Now to configure the advice...
        //切面的名字
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrder);
        //通知注解中的参数名
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        //参数绑定
        springAdvice.calculateArgumentBindings();
        return springAdvice;
    }

上面即是获取Advice的过程。我们简单的看一下calculateArgumentBindings这个方法做了什么事:
calculateArgumentBindings

    public synchronized final void calculateArgumentBindings() {
        //如果已经进行过参数绑定了  或者通知方法中没有参数
        if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
            return;
        }
        
        int numUnboundArgs = this.parameterTypes.length;
        //通知方法参数类型
        Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
        //如果第一个参数是JoinPoint或者ProceedingJoinPoint
        if (maybeBindJoinPoint(parameterTypes[0]) || 
        //这个方法中还有一个校验 即只有在环绕通知中第一个参数类型才能是ProceedingJoinPoint
        maybeBindProceedingJoinPoint(parameterTypes[0])) {
            numUnboundArgs--;
        }
        //如果第一个参数是JoinPoint.StaticPart
        else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
            numUnboundArgs--;
        }

        if (numUnboundArgs > 0) {
            //进行参数绑定 绑定过程略复杂
            //常见的场景是我们使用 后置返回通知和后置异常通知的时候 需要指定 returning和throwing的值 
            bindArgumentsByName(numUnboundArgs);
        }
        this.argumentsIntrospected = true;
    }

通过前面的分析我们可以了解到一个切面中的通知方法会生成一个Advisor实例(如InstantiationModelAwarePointcutAdvisorImpl,其实这个也是我们在SpringAOP中最常用的一个Advisor实现类),在生成这个Advisor实例的过程中会创建一个相应的Advice实例! 一个通知方法---->一个Advisor(包含Pointcut)------>一个Advice!
PS:这里只是一个生成Advice的地方,在其他的地方也会生成Advice,我们在以后再分析。

相关文章
|
10天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
30天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
37 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
16天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
27 1
|
12天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
25 0
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
417 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
69 2
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
127 9
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
168 2
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
102 5