Spring5源码(34)-SpringAop获取增强(二)

本文涉及的产品
云解析DNS-重点域名监控,免费拨测 20万次(价值200元)
简介: Spring5源码(34)-SpringAop获取增强(二)


在上一篇结尾,我们得到了增强的提取工作交给了List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);。接着分析。

1. getAdvisors获取增强简析

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 1、预处理工作,包括获取切面类,名称,验证等
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);
    // 我们需要用一个装饰器包装MetadataAwareAspectInstanceFactory,这样它只会实例化一次。
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    // 2、提取增强
    // 2.1、获取切面类的所有方法,循环判断提取合适的切入点,并创建增强
    List<Advisor> advisors = new ArrayList<>();
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 获取增强
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    // If it's a per target aspect, emit the dummy instantiating aspect.
    // 2.2、处理perthis和pertarget切面类
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        // 创建SyntheticInstantiationAdvisor实例
        Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        // 将SyntheticInstantiationAdvisor实例加入到advisors集合首位,注意:不是替换
        advisors.add(0, instantiationAdvisor);
    }
    // Find introduction fields.
    // 2.3、处理引入,获取所有的引入并循环创建DeclareParentsAdvisor
    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    return advisors;
}

在该方法中提取工作一共分为了三步:提取普通增强、处理处理perthis和pertarget、引介增强。篇幅有限,只分析普通增强的处理过程,该过程也是大家最关心的过程。来看代码:

@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {
    // 1、验证
    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    // 2、提取切入点
    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod,
            aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }
    // 3、创建增强
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

在该方法中,终于看到了核心的切点提取和创建增强。下面分别来看这两步是如何实现的。

2.提取切入点

@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 1、从候选切入点上找出增强表达式
    AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }
    // 2、创建AspectJExpressionPointcut对象
    AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    if (this.beanFactory != null) {
        ajexp.setBeanFactory(this.beanFactory);
    }
    return ajexp;
}
2.1 从候选切入点上找出增强表达式

/**
 * Find and return the first AspectJ annotation on the given method
 * (there <i>should</i> only be one anyway...).
 */
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    Class<?>[] classesToLookFor = new Class<?>[] {
            Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
    for (Class<?> c : classesToLookFor) {
        AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
        if (foundAnnotation != null) {
            return foundAnnotation;
        }
    }
    return null;
}

该过程主要通过findAnnotation方法完成,但是该方法调用较深,也不属于我们分析的范畴,感兴趣的同学可以自己跟踪调试。

2.2 创建AspectJExpressionPointcut对象

/**
 * Create a new AspectJExpressionPointcut with the given settings.
 * @param declarationScope the declaration scope for the pointcut
 * @param paramNames the parameter names for the pointcut
 * @param paramTypes the parameter types for the pointcut
 */
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
    this.pointcutDeclarationScope = declarationScope;
    if (paramNames.length != paramTypes.length) {
        throw new IllegalStateException(
                "Number of pointcut parameter names must match number of pointcut parameter types");
    }
    this.pointcutParameterNames = paramNames;
    this.pointcutParameterTypes = paramTypes;
}

该创建过程比较简单,将提取的切点表达式的信息实例化为AspectJExpressionPointcut对象即可。

3.创建增强

public InstantiationModelAwarePointcutAdvisorImpl(
            AspectJExpressionPointcut declaredPointcut,
            Method aspectJAdviceMethod,
            AspectJAdvisorFactory aspectJAdvisorFactory,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrder,
            String aspectName) {
    this.declaredPointcut = declaredPointcut;
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    this.methodName = aspectJAdviceMethod.getName();
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    this.aspectInstanceFactory = aspectInstanceFactory;
    this.declarationOrder = declarationOrder;
    this.aspectName = aspectName;
    // 1、延迟初始化
    // 在Spring AOP中,切面类的实例只有一个,比如前面我们一直使用的MyAspect类,
    // 假设我们使用的切面类需要具有某种状态,以适用某些特殊情况的使用,比如多线程环境,此时单例的切面类就不符合我们的要求了。
    // 在Spring AOP中,切面类默认都是单例的,但其还支持另外两种多例的切面实例的切面,即perthis和pertarget,
    // 需要注意的是perthis和pertarget都是使用在切面类的@Aspect注解中的。
    // 这里perthis和pertarget表达式中都是指定一个切面表达式,其语义与前面讲解的this和target非常的相似,
    // perthis表示如果某个类的代理类符合其指定的切面表达式,那么就会为每个符合条件的目标类都声明一个切面实例;
    // pertarget表示如果某个目标类符合其指定的切面表达式,那么就会为每个符合条件的类声明一个切面实例。
    // 从上面的语义可以看出,perthis和pertarget的含义是非常相似的。如下是perthis和pertarget的使用语法:
    // perthis(pointcut-expression)
    // pertarget(pointcut-expression)
    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        // Static part of the pointcut is a lazy type.
        Pointcut preInstantiationPointcut = Pointcuts.union(
                aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(),
                this.declaredPointcut);
        // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
        // If it's not a dynamic pointcut, it may be optimized out
        // by the Spring AOP infrastructure after the first evaluation.
        this.pointcut = new PerTargetInstantiationModelPointcut(
                this.declaredPointcut,
                preInstantiationPointcut,
                aspectInstanceFactory);
        this.lazy = true;
    }
    // 2、立刻初始化
    else {
        // A singleton aspect.
        this.pointcut = this.declaredPointcut;
        this.lazy = false;
        // 初始化增强
        this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
}

创建过程中又涉及到perthis和pertarget,无需理会,还是先看单例模式下的创建过程:

/**
 * 根据pointcut初始化增强
 * @param pointcut
 * @return
 */
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    Advice advice = this.aspectJAdvisorFactory.getAdvice(
            this.aspectJAdviceMethod, pointcut,
            this.aspectInstanceFactory,
            this.declarationOrder,
            this.aspectName);
    return (advice != null ? advice : EMPTY_ADVICE);
}

@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    // 1、获取增强之前的处理
    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    validate(candidateAspectClass);
    AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }
    // If we get here, we know we have an AspectJ method.
    // Check that it's an AspectJ-annotated class
    if (!isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" +
                candidateAspectClass.getName() + "]");
    }
    // 2、针对各种不同的增强,做不同的处理
    AbstractAspectJAdvice springAdvice;
    switch (aspectJAnnotation.getAnnotationType()) {
            // 1、前置增强
        case AtBefore:
            springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
            // 2、后置增强
        case AtAfter:
            springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
            // 3、后置返回增强
        case AtAfterReturning:
            springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;
            // 4、后置异常增强
        case AtAfterThrowing:
            springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
            // 5、环绕增强
        case AtAround:
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
            // 6、如果是Pointcut,则不做处理
        case AtPointcut:
            return null;
            // 7、未能满足case条件,抛出异常
        default:
            throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
    }
    // 3、获取增强方法之后,对增强方法进行配置
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();
    return springAdvice;
}

看到这里,是不是有种豁然开朗的感觉,Spring针对各种不同的增强创建过程,做了不同的处理。到这里Spring创建增强的过程就完成了,对于不同的增强类型创建,大家可以自己debug跟踪,这里不一一赘述了。

4.关于Advisor的疑问

这两篇在介绍获取增强,但是最受获取到的并不是Advice而是Advisor,可能大家会有所疑问。举例说明一下:

如果我们定义了一个DogAspect类,并用@AspectJ对其进行注解,那么该类仅仅代表一个切面类,会被Spring扫描并解析,仅此而已,该类不代表SpringAop概念中的切面。那么Spring如果通过解析该类得到具体的切面呢?

首先,关于SpringAop中的切面概念,可以理解为 切面=连接点+增强

其次,而标记了@AspectJ注解的类在被Spring解析的时候,

  1. 提取该类的方法上的切点表达式注解:例如-->@Pointcut("execution(* com.lyc.cn.v2.day07..
    (..))"),解析之后,就可以的到具体的切点.
  2. 提取该类的方法上的增强注解:例如:@Before("test()")解析之后,就可以得到具体的增强代码

最后,通过第一步和第二步的操作,就可以得到切点+增强,那么自然就构成了一个切面

但是Advisor接口里只包含了一个Advice,并且Advisor一般不直接提供给用户使用,所以这里也可以理解为获取增强,当然如果理解为切面也是没有问题的。





目录
相关文章
|
4月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
8月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
504 70
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
359 2
|
9月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
228 0
|
11月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
405 7
|
12月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
286 2
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
295 9
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
876 1
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
516 5