Spring AOP详解

news2025/1/23 17:33:43

1.什么是 Spring AOP?

AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的
集中处理。⽐如⽤户登录权限的效验,没学 AOP 之前,我们所有需要判断⽤户登录的⻚⾯(中的
⽅法),都要各⾃实现或调⽤⽤户验证的⽅法,然⽽有了 AOP 之后,我们只需要在某⼀处配置⼀
下,所有需要判断⽤户登录⻚⾯(中的⽅法)就全部可以实现⽤户登录验证了,不再需要每个⽅
法中都写相同的⽤户登录验证了。
⽽ AOP 是⼀种思想,⽽ Spring AOP 是⼀个框架,提供了⼀种对 AOP 思想的实现,它们的关系和 IoC与 DI 类似。


2.为什要⽤ AOP?

想象⼀个场景,我们在做后台系统时,除了登录和注册等⼏个功能不需要做⽤户登录验证之外,其他⼏乎所有⻚⾯调⽤的前端控制器( Controller)都需要先验证⽤户登录的状态,那这个时候我们要怎么处理呢?
 

我们之前的处理⽅式是每个 Controller 都要写⼀遍⽤户登录验证,然⽽当你的功能越来越多,那么你要写的登录验证也越来越多,⽽这些⽅法⼜是相同的,这么多的⽅法就会代码修改和维护的成本。那有没有简单的处理⽅案呢?答案是有的,对于这种功能统⼀,且使⽤的地⽅较多的功能,就可以考虑 AOP来统⼀处理了.

除了统⼀的⽤户登录判断之外,AOP 还可以实现:

  • 统⼀⽇志记录
  • 统⼀⽅法执⾏时间统计
  • 统⼀的返回格式设置
  • 统⼀的异常处理
  • 事务的开启和提交等
     

也就是说使⽤ AOP 可以扩充多个对象的某个能⼒,所以 AOP 可以说是 OOP(Object Oriented
Programming,⾯向对象编程)的补充和完善。

3.接下来要学习的内容

  1. 学习 AOP 是如何组成的?也就是学习 AOP 组成的相关概念。
  2. 学习 Spring AOP 使⽤。
  3. 学习 Spring AOP 实现原理。

4.AOP 组成

1.切⾯(Aspect)

切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包
括了连接点的定义。


切⾯是包含了:通知、切点和切⾯的类,相当于 AOP 实现的某个功能的集合.


2.连接点(Join Point)

应⽤执⾏过程中能够插⼊切⾯的⼀个点,这个点可以是⽅法调⽤时,抛出异常时,甚⾄修改字段
时。切⾯代码可以利⽤这些点插⼊到应⽤的正常流程之中,并添加新的⾏为。
 

3.切点(Pointcut)

Pointcut 是匹配 Join Point 的谓词。
Pointcut 的作⽤就是提供⼀组规则(使⽤ AspectJ pointcut expression language 来描述)来匹
配 Join Point,给满⾜规则的 Join Point 添加 Advice。

切点相当于保存了众多连接点的⼀个集合(如果把切点看成⼀个表,⽽连接点就是表中⼀条⼀条
的数据)

4.通知(Advice)


切⾯也是有⽬标的 ——它必须完成的⼯作。在 AOP 术语中,切⾯的⼯作被称之为通知。
通知:定义了切⾯是什么,何时使⽤,其描述了切⾯要完成的⼯作,还解决何时执⾏这个⼯作的
问题。

Spring 切⾯类中,可以在⽅法上使⽤以下注解,会设置⽅法为通知⽅法,在满⾜条件后会通知本
⽅法进⾏调⽤:

  • 前置通知使⽤ @Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。
  • 后置通知使⽤ @After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。
  • 返回之后通知使⽤ @AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
  • 抛异常后通知使⽤ @AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
  • 环绕通知使⽤ @Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为。

切点相当于要增强的⽅法。

AOP组成:

1.切面:用来描述某一件事情(切面),类似某个数据库

2.切点:使用一组规则来匹配连接点,类似某张表。

3.连接点:切面中的所有方法,类似有表中的数据(一个连接点就是一条数据)。

4.通知:切面的具体实现

a)前置通知

b)后置通知

c)抛出异常之后通知

d)返回方法之后通知

e)环绕通知

AOP 整个组成部分的概念如下图所示,以多个⻚⾯都要访问⽤户登录权限为例:


 

4.Spring AOP 实现

接下来我们使⽤ Spring AOP 来实现⼀下 AOP 的功能,完成的⽬标是拦截所有 UserController ⾥⾯的⽅法,每次调⽤ UserController 中任意⼀个⽅法时,都执⾏相应的通知事件。

Spring AOP 的实现步骤如下:

  1. 添加 Spring AOP 框架⽀持。
  2. 定义切⾯和切点。
  3. 定义通知
     

添加 AOP 框架⽀持
 

 

 

把 <version>2.7.5</version> 删掉,spring boot自动匹配版本。

定义切⾯和切点
切点指的是具体要处理的某⼀类问题,⽐如⽤户登录权限验证就是⼀个具体的问题,记录所有⽅法的执⾏⽇志就是⼀个具体的问题,切点定义的是某⼀类问题.

Spring AOP 切点的定义如下,在切点中我们要定义拦截的规则,具体实现如下:

切点表达式说明
AspectJ ⽀持三种通配符

  • * :匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数)
  • .. :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使⽤。
  • + :表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如 com.cad.Car+ ,表示继承该类的所有⼦类包括本身

 

切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法为:
execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
修饰符和异常可以省略。
 

表达式示例

  • execution(* com.cad.demo.User.*(..)) :匹配 User 类⾥的所有⽅法。
  • execution(* com.cad.demo.User+.*(..)) :匹配该类的⼦类包括该类的所有⽅法。
  • execution(* com.cad.*.*(..)) :匹配 com.cad 包下的所有类的所有⽅法。
  • execution(* com.cad..*.*(..)) :匹配 com.cad 包下、⼦孙包下所有类的所有⽅法。
  • execution(* addUser(String, int)) :匹配 addUser ⽅法,且第⼀个参数类型是 String,第⼆个参数类型是 int。

第一个 * 表示返回值的类型任意

定义相关通知
通知定义的是被拦截的⽅法具体要执⾏的业务,⽐如⽤户登录权限验证⽅法就是具体要执⾏的业务。Spring AOP 中,可以在⽅法上使⽤以下注解,会设置⽅法为通知⽅法,在满⾜条件后会通知本⽅法进⾏调⽤:

  • 前置通知使⽤@Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。
  • 后置通知使⽤@After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。
  • 返回之后通知使⽤@AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
  • 抛异常后通知使⽤@AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
  • 环绕通知使⽤@Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为

具体实现如下:

package com.example._20230103.commons;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect     //表示当前的类为切面
@Component
public class UserAspect {
    //定义切点,这里使用AspectJ表达式语法
    @Pointcut("execution(* com.example._20230103.controller.UserController.*(..))")
    public void pointcut() {

    }

    //前置通知
    @Before("pointcut()")
    public void before() {
        System.out.println("执行了前置方法!");
    }

    //后置通知
    @After("pointcut()")
    public void after() {
        System.out.println("执行了后置方法!");
    }
    // return 之前通知
    @AfterReturning("pointcut()")
    public void afterReturning() {
        System.out.println("执行了AfterReturning方法!");
    }
    // 抛出异常之前通知
    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("执行了AfterThrowing方法!");
    }

    //添加环绕通知
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = null;
        System.out.println("执行环绕通知的前置方法");
        obj = joinPoint.proceed(); //执行拦截的方法
        System.out.println("执行环绕通知的后置方法");
        return obj;
    }
}

 

 

启动项目,运行结果:

 

 

 

 

5.Spring AOP 实现原理

Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的⽀持局限于⽅法级别的拦截

Spring AOP ⽀持 JDK Proxy 和 CGLIB ⽅式实现动态代理。默认情况下,实现了接⼝的类,使⽤
AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。
 

织⼊(Weaving):代理的⽣成时机
织⼊是把切⾯应⽤到⽬标对象并创建新的代理对象的过程,切⾯在指定的连接点被织⼊到⽬标对
象中.

在⽬标对象的⽣命周期⾥有多个点可以进⾏织⼊:

  • 编译期:切⾯在⽬标类编译时被织⼊。这种⽅式需要特殊的编译器。AspectJ的织⼊编译器就是以这种⽅式织⼊切⾯的。
  • 类加载器:切⾯在⽬标类加载到JVM时被织⼊。这种⽅式需要特殊的类加载器(ClassLoader),它可以在⽬标类被引⼊应⽤之前增强该⽬标类的字节码。AspectJ5的加载时织⼊(load-time weaving. LTW)就⽀持以这种⽅式织⼊切⾯。
  • 运⾏期:切⾯在应⽤运⾏的某⼀时刻被织⼊。⼀般情况下,在织⼊切⾯时,AOP容器会为⽬标对象动态创建⼀个代理对象。SpringAOP就是以这种⽅式织⼊切⾯的。

动态代理

此种实现在设计模式上称为动态代理模式,在实现的技术⼿段上,都是在 class 代码运⾏期,动
态的织⼊字节码。

我们学习 Spring 框架中的AOP,主要基于两种⽅式:JDK 及 CGLIB 的⽅式。这两种⽅式的代理
⽬标都是被代理类中的⽅法,在运⾏期,动态的织⼊字节码⽣成代理类

  • CGLIB是Java中的动态代理框架,主要作⽤就是根据⽬标类和⽅法,动态⽣成代理类。
  • Java中的动态代理框架,⼏乎都是依赖字节码框架(如 ASM,Javassist 等)实现的。
  • 字节码框架是直接操作 class 字节码的框架。可以加载已有的class字节码⽂件信息,修改部分信息,或动态⽣成⼀个 class。

JDK 动态代理实现

JDK 实现时,先通过实现 InvocationHandler 接⼝创建⽅法调⽤处理器,再通过 Proxy 来创建代
理类。

JDK 和 CGLIB 实现的区别

  1. JDK 实现,要求被代理类必须实现接⼝,之后是通过 InvocationHandler 及 Proxy,在运⾏时动态的在内存中⽣成了代理类对象,该代理对象是通过实现同样的接⼝实现(类似静态代理接⼝实现的⽅式),只是该代理类是在运⾏期时,动态的织⼊统⼀的业务逻辑字节码来完成。
  2. CGLIB 实现,被代理类可以不实现接⼝,是通过继承被代理类,在运⾏时动态的⽣成代理类对象。

总结:

Spring AOP 是通过动态代理的⽅式,在运⾏期将 AOP 代码织⼊到程序中的,它的实现⽅式有两种:JDK Proxy 和 CGLIB。

未完待续~



 



 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YACC移进规约冲突案例分析(二)output中状态机转移步骤详解

案例 calc.y %union {int ival;const char *sval; } %token <ival> NUM %nterm <ival> exp %token <sval> STR %nterm <sval> useless %left - %left * %% exp:exp exp | exp - exp | exp * exp | exp / exp | NUM ; useless: STR; %%编译 $ biso…

恭喜龙蜥获得中国开源云联盟2022年度中国“最佳开源实践案例”和“杰出开源贡献者”奖项

近日&#xff0c;由工信部中国电子技术标准化研究院主办的 2022 木兰峰会在北京圆满举办&#xff0c;峰会上正式公布了中国开源云联盟(China Open Source Cloud League&#xff0c;简称“COSCL”) 2022 年度评选名单&#xff0c;龙蜥社区荣获中国“最佳开源实践案例”和“杰出开…

仪器设备使用

NI DcpowerSwitchDigitalDMMFgenScope名称直流电源&#xff08;SMU&#xff09;继电器PPMU数字万用表信号发生器示波器版本PXI-4147PXI-2567PXI-6571PXI-4070PXI-4463PXI-5160 1.Scope 示波器是一种电子测量仪器&#xff0c;可以在无干扰的情况下监控输入信号&#xff0c;随后…

Go结构体(struct)

文章目录Struct定义struct构造struct实例struct的值和指针在与函数共用时&#xff1a;匿名字段和嵌套struct嵌套struct的名称冲突问题Struct 是一个值类型的 定义struct type identifier struct {field1 type1field2 type2… } // 或者 type T struct { a, b int }理论上&am…

JAVA多线程初阶(1)

目录JAVA多线程(1)1.Thread类创建与使用1.1 继承Thread类1.2 实现并发关于sleep()1.3 Runnable创建线程1.4 匿名内部类创建线程1.5 lamda表达式创建线程2.多线程提高效率3.Thread类属性和方法3.1 Thread(String name)3.2 isDaemon()3.3 isAlive()3.3 线程的重要方法3.4 中断线程…

数据结构:图

文章目录图内存中存储图数据结构邻接矩阵存储方法用邻接矩阵&#xff08;Adjacency Matrix&#xff09;来表示一个图的缺点&#xff1a;浪费空间优点邻接表存储方法&#xff08;Adjacency List&#xff09;广度优先算法Breadth-First-Search&#xff08;BFS&#xff09;深度优先…

Android——GT库-日志工具

GT库在创造出来初期&#xff0c;里面的日志工具就一直存在的&#xff0c;经历了很久的迭代变更&#xff0c;当目前的最新版本&#xff0c;日志工具已经创造出更高级的调试日志方式了&#xff0c;接下来咋们来看看GT库中的日志工具具体使用方法吧。 使用GT库里的&#xff0c;当然…

web表单设计器的优点体现在哪?

在数字化管理越来越规范的当下&#xff0c;拥有一款优质高效的低代码开发平台&#xff0c;确实能给企业提质增效带来更大的帮助。很多客户朋友会问道&#xff1a;web表单设计器都有哪些特点&#xff1f;为什么能在企业的现代化办公管理中起到巨大的作用&#xff1f;今天&#x…

Linux终端远程工具xshell,xftp,mobasterm

目录 软件介绍 1.xshell 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第5步&#xff1a; 2.xftp 第一步&#xff1a; 第二部&#xff1a; 第三步&#xff1a; 3.mobasterm 全能终端神器——MobaXterm 第一步&#xff1a; 第二步&a…

C1083无法打开包括文件: “atlbase.h”: No such file or directory

在打开别人的项目的过程中遇到了“atlbase.h”无法打开的问题&#xff0c;在此记录一下。1.下载ATL生成工具与缓解只下载ATL生成工具后面还会报错&#xff0c;直接下载下载ATL生成工具与缓解一步到位。下载的入口在&#xff1a;工具--->获取工具与功能。需要注意的是&#x…

Guitar Pro2023Win/Mac中文吉他/贝斯打谱识谱软件

Guitar Pro 是一款曲谱阅读器。以 GTP 结尾的曲谱文件都必须用 Guitar Pro 才能打开。Guitar Pro 凭借着其便利的制谱和读曲谱环境&#xff0c;在各大谱库论坛里都占据着一席之地&#xff0c;喜欢吉他的朋友一定略有耳闻。早几年该作者将它移植到了移动平台&#xff0c;现在你也…

7-2国王游戏

题目&#xff1a; 恰逢 H 国国庆&#xff0c;国王邀请 n 位大臣来玩一个有奖游戏。 首先&#xff0c;他让每个大臣在左、右手上面分别写下一个整数&#xff0c;国王自己也在左、右手上各写一个整数。 然后&#xff0c;让这 n 位大臣排成一排&#xff0c;国王站在队伍的最前面。…

应用层——Web和HTTP

目录 1. HTTP概况 1.1 Web页面简介 1.2 URL-统一资源定位器 1.3 HTTP协议 2. HTTP连接的两种类型 2.1 HTTP非持久性连接(Non-persistent HTTP) 2.2 HTTP持久性连接(Persistent HTTP) 2.2.1 无流水(pipelining)的持久性连接 2.2.2 带有流水机制的持久性连接 3. HT…

一站式开发平台赋能办公全场景

近几年&#xff0c;数字化办公迎来了新的机遇&#xff0c;根据亿欧智库《2022中国数字化办公市场研究报告》推算&#xff0c;数字化办公2021年的市场规模达到973.89亿元&#xff0c;至2025年将达到1768.16亿元&#xff0c;整体增速保持平稳&#xff0c;2018-2025年的CAGR为15.8…

Mybatis 框架搭建封装JDBC,实现sql语句

目录 1、maven新建一个工程​编辑 2、添加POM.XML配置文件 3、创建实例包 4、创建一个环境资源根目录 5、配置环境文件 6、创建接口&#xff0c;添加方法 7、编写sql语句 8、创建测试类 8.1 、定义工厂模式 8.2 、定义会话 8.3、定义对象 8.5、获取Builder建造工厂 …

LAB3 EIGRP1实验

1 实验拓扑&#xff1a; 2 实验要求&#xff1a; 1>.R1-R3环回口0:192.168.100.x/32。 2>.R1上采用手动汇总的命令&#xff0c;汇总4条环回口成一条。 3>.R1上下发一条默认路由。 4>.实现R1到R2的环回口路由非等价负载。 5>.as 90都使用eigrp认证。 6>…

css动画效果之transition

transition-property规定设置过渡效果的 CSS 属性的名称。属性名属性值none没有属性会获得过渡效果。all所有属性都将获得过渡效果。property定义应用过渡效果的 CSS 属性名称列表&#xff0c;列表以逗号分隔。使用方式transition-property: width,background;/* 多个效果可用逗…

设计模式之装饰模式

1.前言 装饰模式&#xff1a;动态的给一个类添加一些额外职责&#xff0c;就增加功能来说&#xff0c;装饰模式比生成子类更加灵活。 装饰模式属于结构型模式&#xff0c;它是作为现有的 类的⼀个包装&#xff0c;允许向⼀个现有的对象添加新的功能&#xff0c; 同时⼜不改变其…

Spring创建和使用 (存储和读取) -- 1

Spring创建和使用 存储和读取 -- 1一、创建 Spring 项目1.1 创建⼀个 Maven 项目1.2 添加 Spring 框架支持1.3 添加启动类二、存储 Bean 对象2.1 创建 Bean2.2 将 Bean 注册到容器三、获取并使用 Bean 对象3.1 创建 Spring 上下文3.2 获取指定的 Bean 对象3.3 使用 Bean四、总结…

数据结构-第六期——并查集(Python)

目录 认识并查集 经典应用: 应用场景 并查集的操作 初始化 代码实现 合并 代码实现 查找 代码实现 查找代码【图解】 有多少个集&#xff08;帮派&#xff09;? 复杂度 查询的优化:路径压缩 【代码】用递归实现 并查集:初始化、查找、合并代码 蓝桥杯…