Spring AOP

AOP 的本质:在不改变原有逻辑的情况下,增强横切的逻辑,横切逻辑代码往往是权限校验代码、日志代码、事务控制代码、性能监控代码。

Spring AOP(面向切面编程,Aspect-Oriented Programming)是Spring框架的一部分,提供了基于代理的AOP功能。它允许你在不改变原有代码的情况下,向应用程序中添加额外的功能或行为。

核心概念

切面(Aspect)

切面是AOP的核心概念,表示横切关注点的模块化。切面是由切点和通知组成的,切面定义了哪些方法会应用额外的功能(通知)。

连接点(Joinpoint)

连接点表示程序执行中的一个点,比如方法调用、方法执行、构造函数调用等。Spring AOP中的连接点通常是方法执行的地方。

切点(Pointcut)

切点定义了哪些连接点是需要拦截的,它通常是基于方法签名进行筛选的。例如,选择某个包下所有的方法,或者某个类的方法。

通知(Advice)

通知定义了在连接点上执行的代码,通知决定了增强的具体行为。通知有不同的类型:

  • 前置通知(Before):方法执行前执行
  • 后置通知(After):方法执行后执行,不论方法是否抛出异常
  • 返回通知(AfterReturning):方法正常执行完后执行
  • 异常通知(AfterThrowing):方法抛出异常时执行
  • 环绕通知(Around):可以控制方法的执行,既可以选择执行方法,也可以选择不执行方法,或者修改方法的返回值

目标对象(Target Object)

目标对象是被AOP代理的对象,它是切面操作的实际对象。

Spring AOP的工作原理

Spring AOP基于代理模式来实现,它主要通过两种方式生成代理对象:

  • JDK动态代理:适用于目标对象实现了接口的情况
  • CGLIB代理:适用于目标对象没有实现接口的情况

AOP 术语

业务主线

在讲解 AOP 术语之前,我们先来看一下这些图。上图描述的就是未采用的 AOP 思想的设计的程序,当我们红色框中圈定的方法时,会带来大量的重复劳动,程序中充斥着大量的重复代码。而下图中采用了 AOP 思想涉及的程序,它把红框部门的代码抽取出来的同时,运用动态代理技术,在运行期间对需要使用业务逻辑方法进行增强。

AOP术语详解

  • JoinPoint 连接点:它指的是那些可以用于增强代码加入到业务主线中的点,这些点指的就是方法。在方法执行的前后通过动态代理的技术加入增强的代码。在 Spring 的框架 AOP思想的技术实现中,也只支持方法类型的连接点。
  • Pointcut 切点:它指的是已经增强的代码加入业务主线进行之后的连接点,由上图中,我们看出表现层 transfer 方法就只是连接点,因为判断访问权限的功能并没有对其进行增强。
  • Advice 通知增强:它指的是切面中用于提供增强功能的方法,并且不同方法增强的时机是不一样的。比如,开启事务肯定是在要在业务方法执行之前的,提交事务要肯定在业务方法执行之后的,而回滚的执行需要在业务方法出现错误的时候再执行。这些就是通知类型,目前的分类有:前置通知、后置通知、异常通知、最终通知、环绕通知。
  • Target 目标对象:它指的是代理的目标对象,即被代理对象
  • Proxy 代理对象:它指的是一个类被 AOP 织入增强后,产生的代理类,即代理对象。
  • Weaving 织入:它指的是增强应用到目标对象来创建新的代理的过程,Spring 采用动态代理织入,而 AspectJ 采用编译期间织入和类装载期间织入。
  • Aspect 切面:它指定是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面。

AOP 代理选择

Spring 实现 AOP 思想使用的动态代理技术,默认情况下,Spring 会根据被代理对象是否实现接口来选择JDK 还是 CGLIB,当被代理对象实现了接口,Spring 会选择 JDK 官方的代理技术,不过我们可以通过配置的方式,让 Spring 强制使用 CGLIB 的方式。


AOP 配置方式

在 Spring 的 AOP 的配置中,和 IoC 配置一样,支持三类的配置方式:

  • 使用 XML 配置
  • 使用 XML + 注解的方式配置
  • 使用纯注解的方式配置

AOP 实现

需求描述

横切逻辑代码是打印日志,希望打印日志的逻辑织入到目标方法的特定位置

添加依赖

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aop</artifactId>
 <version>5.1.12.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.4</version>
</dependency>

核心配置

我们在基于 XML 的配置步骤如下:

  1. 把通知的 Bean 交给 Spring 管理
  2. 使用 aop:config 开始 AOP 的配置
  3. 使用 aop:aspect 配置切面
  4. 使用对应的标签配置通知的类型
<!-- 日志工具 交由给 Spring 进行管理 -->
<bean id="wzkLogUtils" class="wzk.utils.WzkLogUtils"></bean>
<!-- AOP 配置 -->
<aop:config>
    <!-- 配置切面 -->
    <aop:aspect id="wzkLogAdvice" ref="wzkLogUtils">
        <aop:before method="printLog" pointcut="execution(public * wzk.service.impl.WzkTransferServiceImpl.update(wzk.model.WzkAccount))"></aop:before>
    </aop:aspect>
</aop:config>

切入点表达式

切入点表达式也叫做 AspectJ 切入点表达式,指的是遵循特定的语法结构的字符串,其作用的是用于对符合的语法格式的连接点进行增强,它是 AspectJ 的一部分。

改变代理的配置

改变代理的配置,Spring 在选择创建代理的时候,会根据被代理对象实际情况来选择的,被代理对象实现了接口,则采用基于接口的动态代理,当被代理对象没有实现任何接口的时候,Spring 会自动切换到基于子类的动态代理方式。

配置方式有两种:

  • aop:config 标签来配置 proxy-target-class=“true”
  • aop:aspectj-autoproxy 标签来配置 proxy-target-class=“true”