简介

spring-aop 用于生成动态代理类(底层是使用 JDK 动态代理或 cglib 来生成代理类),搭配 spring-bean 一起使用,可以使 AOP 更加解耦、方便。在实际项目中,spring-aop 被广泛用来实现日志、权限、事务、异常等的统一管理。

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

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

项目环境

maven:3.6.3

操作系统:win10

JDK:8u231

Spring:5.2.6.RELEASE

一点补充

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

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

最后,和以往不同,

几个重要的组件

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

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

对Joinpoint做Advice

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

└─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是一个“动态”的概念,FieldMethod、或Constructor等对象是它的静态部分。

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

在 spring-aop 中,主要使用Joinpoint的子接口--MethodInvocation,JDK 动态代理使用的MethodInvocation实现类为ReflectiveMethodInvocation,cglib 使用的是MethodInvocation实现类为CglibMethodInvocation

  1. Advice

Joinpoint执行的某些操作

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

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

为了更好地理解这两个概念,我再举一个例子:当我们对用户进行增删改查前,进行权限校验。其中,调用用户的新增方法的事件就是一个的Joinpoint,权限校验就是一个Advice,即对JoinpointAdvice

在 spring-aop 中,Joinpoint对象持有了一条Advice chain,调用Joinpointproceed()方法将采用责任链的形式依次执行(注意,Advice的执行可以互相嵌套,不是单纯的先后顺序)。

其他的几个概念

在 spring-aop 中,还会使用到其他的概念,例如Advice FilterAdvisorPointcutAspect等。

  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

@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 图来简单说明。

AdvisedSupport用来告诉AopProxy如何生成代理对象,它描述了两部分信息:

  1. 对谁生成代理对象?--TargetSourceTargetSource既可以返回单例对象,也可以返回多例对象,有点类似于我们常用的DataSource
  2. 生成的代理对象持有的 Advisor List。前面提到过,当我们执行代理方法时,将会采用责任链的方式执行Advice chain,而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

本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/13745168.html

Spring源码系列(四)--spring-aop是如何设计的的更多相关文章

  1. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

  2. Spring源码系列 — BeanDefinition扩展点

    前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition ...

  3. Spring源码系列 — Bean生命周期

    前言 上篇文章中介绍了Spring容器的扩展点,这个是在Bean的创建过程之前执行的逻辑.承接扩展点之后,就是Spring容器的另一个核心:Bean的生命周期过程.这个生命周期过程大致经历了一下的几个 ...

  4. Spring源码系列 — BeanDefinition

    一.前言 回顾 在Spring源码系列第二篇中介绍了Environment组件,后续又介绍Spring中Resource的抽象,但是对于上下文的启动过程详解并未继续.经过一个星期的准备,梳理了Spri ...

  5. 事件机制-Spring 源码系列(4)

    事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...

  6. Ioc容器依赖注入-Spring 源码系列(2)

    Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...

  7. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  8. Ioc容器beanDefinition-Spring 源码系列(1)

    Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...

  9. Spring源码系列(二)--bean组件的源码分析

    简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...

随机推荐

  1. python自动化测试中的数据驱动unittest+ddt

    ddt是一个unittest的插件,用来实现uniitest的数据驱动 本文以python自动化测试中的数据驱动为原则,记录学习ddt的过程 一.数据的传递规则

  2. DNSPod 修改NS 服务器?

    其实我几乎没在国内注册过域名,更没想过用国内的DNS 服务,DNSPod 也是属于听说过名字的地步而已,但是正好在腾讯云注册了一个cn 域名,又觉得对DNSPod 的DNS 服务不是特别满意,所以想把 ...

  3. 哇,ElasticSearch多字段权重排序居然可以这么玩

    背景 读者提问:ES 的权重排序有没有示列,参考参考? 刚好之前也稍微接触过,于是写了这篇文章,可以简单参考下. 在很多复杂的业务场景下,排序的规则会比较复杂,单一的降序,升序无法满足日常需求.不过 ...

  4. CentOS7(Linux)源码安装Redis

    介绍 项目中经常需要用到Redis做缓存数据库,可是还有小伙伴不会在Linux上安装Redis,毕竟我们开发的项目都是要在服务器上运行的,今天就来讲讲如何在CentOS7环境使用源码进行安装Redis ...

  5. JAVA虚拟机故障诊断总结

    一.JAVA运行时数据区               1.堆(-Xmx与-Xms):所有线程共享.  目的:用来存放对象实例.所有对象实例和数组都要在堆上分配内存.JAVA堆是垃圾收集器管理的主要区域 ...

  6. 开启gzip压缩/cdn是否会影响抓取和收录量

    http://www.wocaoseo.com/thread-291-1-1.html 服务器开启gzip压缩是否会影响蜘蛛抓取和收录量?站点开了CDN,对百度SEO影响有多大?我发现我们站自从开了C ...

  7. 项目实战:Qt+Ffmpeg+OpenCV相机程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  8. kali linux 开启ssh服务

    kali linux 一般默认不开启ssh服务,可使用命令查看ssh服务是否开启 命令:service ssh status 如果显示ssh服务没有开启需要修改ssh配置文件将ssh服务开启,kali ...

  9. 原生JDK网络编程BIO

    服务端提供IP和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信. 传统的同步阻塞模型开发中,ServerSocket负责绑 ...

  10. apply用法

    result.push.apply(result, document.getElementsByTagName(tag)); 但是,这里为什么要用apply呢? 因为document.getEleme ...