Spring之AOP源码分析

2021/11/21 14:39:50

本文主要是介绍Spring之AOP源码分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

上一篇博客简单讲了 spring-aop 的基础组件、架构和使用方法,本文将开始研究 spring-aop 的源码,主要分成以下部分:

  1. spring-aop 的几个重要的组件,如 Joinpoint、Advice、Pointcut、Advisor 等;
  2. spring-aop 是如何设计的

一点补充

在上一篇博客中,我们使用 spring-aop 提供的代理工厂来生成动态代理类,被代理的对象可以是我们自己 new 的一个对象,也可以是 bean。因为 spring-aop 最主要的功能就是生成动态代理类,所以,本文的源码分析都只围绕这个功能展开,并不会掺杂 spring-bean 的内容。

实际项目中,Spring 可以“悄无声息”地完成对 bean 的代理,本质是通过注册BeanPostProcessor来实现,原理并不复杂。如果你对 spring-bean 感兴趣的话,可以参考博客。

几个重要的组件

说到 spring-aop,我们经常会提到JoinpointAdvicePointcutAspectAdvisor等等概念,它们都是抽象出来的“标准”,有的来自 aopalliance,有的来自 AspectJ,也有的是 spring-aop 原创。

它们是构成 spring-aop “设计图”的基础,理解它们非常难,一个原因是网上能讲清楚的不多,第二个原因是这些组件本身抽象得不够直观(spring 官网承认了这一点)。

对Joinpoint做Advice

在 spring-aop 的包中内嵌了 aopalliance 的包(aopalliance 就是一个制定 AOP 标准的联盟、组织),这个包是 AOP 联盟提供的一套“标准”,提供了 AOP 一些通用的组件,包的结构大致如下。

powershell
└─org
    └─aopalliance
        ├─aop
        │      Advice.class
        │      AspectException.class
        │
        └─intercept
                ConstructorInterceptor.class
                ConstructorInvocation.class
                Interceptor.class
                Invocation.class
                Joinpoint.class
                MethodInterceptor.class
                MethodInvocation.class

使用 UML 表示以上类的关系,如下。可以看到,这主要包含两个部分:JoinpointAdvice(这是 AOP 最核心的两个概念)。完整的 aopalliance 包,除了 aop 和 intercept,还包括了 instrument 和 reflect,后面这两个部分 spring-aop 没有引入,这里就不说了。

 

  1. Joinpoint

Joinpoint表示对某个方法(构造方法或成员方法)或属性的调用。

例如,我调用了 user.save() 方法,这个调用动作就属于一个JoinpointJoinpoint是一个“动态”的概念,FieldMethodConstructor等对象是它的静态部分。

如上图所示,JoinpointAdvice操作的对象。

在 spring-aop 中,主要使用Joinpoint的子接口--MethodInvocation

  1. Advice

Joinpoint执行的某些操作。

例如,JDK 动态代理使用的InvocationHandler、cglib 使用的MethodInterceptor,在抽象概念上可以算是Advice(即使它们没有继承Advice)。

在 spring-aop 中,主要使用Advice的子接口--MethodInterceptor

为了更好地理解这两个概念,我再举一个例子:当我们对用户进行新增操作前,需要进行权限校验。其中,调用 user.save() 的动作就是一个的Joinpoint,权限校验就是一个Advice,即对Joinpoint(新增用户的动作)做Advice(权限校验)。

在 spring-aop 中,Joinpoint对象持有了一条 Advice chain ,调用Joinpointproceed()方法将采用责任链的形式依次执行各个 Advice(注意,Advice的执行可以互相嵌套,不是单纯的先后顺序)。cglib 和 JDK 动态代理的缺点就在于,它们没有所谓的 Advice chain,一个Joinpoint一般只能分配一个Advice,当需要使用多个Advice时,需要像套娃一样层层代理。

其他的几个概念

在 spring-aop 中,还会使用到其他的概念,例如Advice FilterAdvisorPointcutAspect等。这些概念的重要性不如JoinpointAdvice,如果对深入了解 spring-aop 不感兴趣的话,可以不用了解。

  1. Advice Filter

Advice Filter 一般和Advice绑定,它用来告诉我们,Advice是否作用于指定的Joinpoint,如果 true,则将Advice加入到当前JoinpointAdvice chain,如果为 false,则不加入。

在 spring-aop 中,常用的 Advice Filter 包括ClassFilterMethodMatcher,前者过滤的是类,后者过滤的是方法。

  1. Pointcut

Pointcut是 AspectJ 的组件,它是一种 Advice Filter。

在 spring-aop 中,Pointcut=ClassFilter+MethodMatcher+。

  1. Advisor

Advisor是 spring-aop 原创的组件,一个 Advisor = 一个 Advice Filter + 一个 Advice。

在 spring-aop 中,主要有两种AdvisorIntroductionAdvisorPointcutAdvisor。前者为ClassFilter+Advice,后者为Pointcut+Advice

  1. Aspect

Aspect也是 AspectJ 的组件,一组同类的PointcutAdvisor的集合就是一个Aspect

在下面代码中,printRequest 和 printResponse 都是Advice,genericPointCut 是Pointcut,printRequest + genericPointCut 是PointcutAdvisor,UserServiceAspect 是Aspect

java
@Aspect
public class UserServiceAspect {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceAspect.class);
    
    @Pointcut("execution(* cn.zzs.spring.UserService+.*(..)))")
    public void genericPointCut() {

    }
    
    @Before(value = "genericPointCut()")
    public void printRequest(JoinPoint joinPoint) throws InterruptedException {
        //······
    }  
    
    @After(value = "genericPointCut()")
    public void printResponse(JoinPoint joinPoint) throws InterruptedException {
      //······;
    }  
}

spring-aop是如何设计的

了解了 spring-aop 的重要组件,接下来就可以构建它的设计视图。spring-aop 的设计视图主要包括两个部分:生成代理类和代理方法的执行。

生成代理类

这里我画了一张 UML 图来简单说明。

 

spring-aop 采用动态代理为目标类生成一个代理对象,Joinpoint 的组装和 advice chain 的执行都是在这个代理对象中完成,而不是通过层层代理的方式来实现。

AdvisedSupport里持有了一条 advice chain,严格来说应该是 advisor List。

AopProxy用来生成代理对象,spring-aop 提供了 JDK 动态代理和 cglib 动态代理两种AopProxy实现。

除此之外,spring-aop 提供了三种代理工厂供调用者使用,其中ProxyFactory比较普通,AspectJProxyFactory支持 AspectJ 语法的代理工厂,ProxyFactoryBean可以给 Spring IoC 管理的 bean 进行代理。上一篇博客已介绍过如何使用这三个代理工厂。

代理方法的执行

这里使用 cglib 的代理类来简单说明代理方法的执行过程。关于 cglib 的内容可以参考: 源码详解系列(一)--cglib动态代理的使用和分析

 

 当我们调用代理的方法时,代理方法中将生成一个Joinpoint对象--即图中的CglibMethodInvocation,它持有了一条Advice chain,而Advice chain通过 Advisor List 过滤得到,调用Joinpointproceed()方法就可以执行Advice chain

以上简单介绍了 spring-aop 的设计视图,有了这些,相信以后会更容易读懂具体的源码。



这篇关于Spring之AOP源码分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程