Spring系列之AOP分析之获取Advisor的过程续(三)

简介:

我们在这一章中继续上一章的分析。我们在上一章中说到了获取到了切面类中所有不带@Pointcut注解的方法,我们看看Spring对我们获取到的这些方法进行了什么操作:

for (Method method : getAdvisorMethods(aspectClass)) {
    //循环切面中所有不带@Pointcut注解的方法。 
    //method 切面中不带@Pointcut注解的方法
    //lazySingletonAspectInstanceFactory中含有切面实例 是个单例
    //advisors中的下标 切面的一个顺序
    //aspectName 切面类的名字
    //下面我们来看一下getAdvisor这个方法的内容
    Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
    if (advisor != null) {
        advisors.add(advisor);
    }
}

getAdvisor

    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrderInAspect, String aspectName) {
        //验证切面类 见我们上一章的分析
        validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
        //这里根据传入的方法和切面类获取 切点表达式
        AspectJExpressionPointcut expressionPointcut = getPointcut(
                candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
        //没有获取到切点表达式 直接返回null
        if (expressionPointcut == null) {
            return null;
        }
        //返回一个Advisor的实例 这个实例中包含了 一下内容
        //切点表达式 AspectJExpressionPointcut 
        //切点方法 
        //ReflectiveAspectJAdvisorFactory实例
        //切面类实例
        //切面类名字
        return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }

我们先来看一下AspectJExpressionPointcut这个类的UML类图:
AspectJExpressionPointcut
在AspectJExpressionPointcut这个类中主要实现了以下这四大接口:ClassFilter、BeanFactoryAware、MethodMatcher、Pointcut。Pointcut是SpringAOP中定义的接口,用来表示切面的抽象。BeanFactoryAware也是SpringIOC中一个常见的接口,用来设置BeanFactory实例。ClassFilter和MethodMatcher是SpringAOP中定义的进行Advisor匹配的接口。ClassFilter用来此Advisor是否使用于目标类。MethodMatcher用来匹配此Advisor是否可以作用于目标类中的目标方法。那么AspectJExpressionPointcut这个类就拥有了一下功能:从BeanFactory中获取Bean、拥有切点表达式、可以用来判断此切点表达式方法是否适用于目标类、此切点表达式方法是否适用于目标类中的方法。OK,我们继续看生成AspectJExpressionPointcut的过程。

    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        //方法上是否存在切面注解(通知类型注解) 即方法上是否有
        //@Before, @Around, @After, @AfterReturning, @AfterThrowing, @Pointcut注解
        //这里也提供了一种获取类上是否有我们想要的注解的一种方式
        //返回一个AspectJAnnotation对象
        //这里用了AnnotationUtils用来获取注解的相关信息
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
        //创建AspectJExpressionPointcut对象
        AspectJExpressionPointcut ajexp =
                new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
        //设置切面表达式 在AspectJAnnotation中是可以获取到通知类型的,但是这里没有设置通知类型
        ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
        ajexp.setBeanFactory(this.beanFactory);
        return ajexp;
    }

在这里我们要看一下AspectJAnnotation这个类。这个类是AbstractAspectJAdvisorFactory中的内部类。从这个类中可以获取切点表达式和通知类型。

    protected static class AspectJAnnotation<A extends Annotation> {
        //切点表达式所在的属性  poincut会覆盖value的值 
        //其实这里指的是 通知类型注解中的属性
        private static final String[] EXPRESSION_PROPERTIES = new String[] {"value", "pointcut"};

        private static Map<Class<?>, AspectJAnnotationType> annotationTypes =
                new HashMap<Class<?>, AspectJAnnotationType>();

        static {
            //初始化通知类型
            annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
            annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
            annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
            annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
            annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
            annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
        }
        .......
        public AspectJAnnotation(A annotation) {
            //注解信息
            this.annotation = annotation;
            //根据注解类型获取通知类型
            this.annotationType = determineAnnotationType(annotation);
            // We know these methods exist with the same name on each object,
            // but need to invoke them reflectively as there isn't a common interface.
            try {
                //从通知类型注解上面获取切点表达式
                this.pointcutExpression = resolveExpression(annotation);
                //获取参数的名字
                this.argumentNames = (String) annotation.getClass().getMethod("argNames").invoke(annotation);
            }
            catch (Exception ex) {
                throw new IllegalArgumentException(annotation + " cannot be an AspectJ annotation", ex);
            }
        }
        //从这个获取切点表达式的代码中我们可以看到 pointcut的属性会覆盖value的属性值
        private String resolveExpression(A annotation) throws Exception {
            String expression = null;
            //循环上面的切点属性
            for (String methodName : EXPRESSION_PROPERTIES) {
                Method method;
                try {
                    method = annotation.getClass().getDeclaredMethod(methodName);
                }
                catch (NoSuchMethodException ex) {
                    method = null;
                }
                if (method != null) {
                    //获取切点表达式!!!
                    String candidate = (String) method.invoke(annotation);
                    if (StringUtils.hasText(candidate)) {
                        expression = candidate;
                    }
                }
            }
            return expression;
        }

我们把上面获取Advisor的过程总结一下:循环切面类中的所有不带@Pointcut注解的方法,接着判断切面类的方法上是否有:@Before, @Around, @After, @AfterReturning, @AfterThrowing, @Pointcut注解。如果没有的话,循环下一个方法。如果有这些注解的话,则从这些注解中获取切点表达式存放到AspectJExpressionPointcut对象中,最后将获取到的切点表达式类封装到InstantiationModelAwarePointcutAdvisorImpl这个类中。从上面的分析我们知道InstantiationModelAwarePointcutAdvisorImpl类至少拥有:切点表达式类、切面对象、带有切点表达式的方法、ReflectiveAspectJAdvisorFactory实例。那么我们最后来分析一下InstantiationModelAwarePointcutAdvisorImpl这个类。

    public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
            Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        //切点表达式类 AspectJExpressionPointcut 
        this.declaredPointcut = declaredPointcut;
        //切面类 
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
        //切点表达式方法所在的方法名 这里指的是@Before、@After这些通知类型所在的方法名
        this.methodName = aspectJAdviceMethod.getName();
        //通知参数类型
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
        //切面通知方法
        this.aspectJAdviceMethod = aspectJAdviceMethod;
        //ReflectiveAspectJAdvisorFactory实例
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
        //切面对象实例
        this.aspectInstanceFactory = aspectInstanceFactory;
        //切面顺序
        this.declarationOrder = declarationOrder;
        this.aspectName = aspectName;
        //根据切面元数据判断是否要延迟实例化 一般为否
        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            // Static part of the pointcut is a lazy type.
            Pointcut preInstantiationPointcut = Pointcuts.union(
                    aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
            this.pointcut = new PerTargetInstantiationModelPointcut(
                    this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
            this.lazy = true;
        }
        else {
            // A singleton aspect.
            //切点表达式类
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            //这里获取Advice实例 这里又拥有了Advice的实例!!!!
            //不得不说InstantiationModelAwarePointcutAdvisorImpl这个类真的是太强大了
            this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
        }
    }

OK下面我们的分析就到了如果从InstantiationModelAwarePointcutAdvisorImpl中获取Advice的内容。

相关文章
|
9月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
10月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
876 0
|
11月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
1446 0
|
11月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1698 13
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
1834 0
|
9月前
|
Java 测试技术 数据库连接
【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!
Spring专栏第四章,本文带你上手 SpringBoot 的文件上传、异常处理、组件注入等功能 并且为你演示Junit5的基础上手体验
1181 3
|
9月前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
650 4
|
缓存 JSON 前端开发
第07课:Spring Boot集成Thymeleaf模板引擎
第07课:Spring Boot集成Thymeleaf模板引擎
1009 0
第07课:Spring Boot集成Thymeleaf模板引擎