【Spring AOP】面向切面编程

news2024/11/13 12:16:28

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

欢迎志同道合的朋友一起加油喔🤺🤺🤺


目录

1. 什么是Spring AOP?

2. 为什么要使用AOP?

3. AOP相关组成的概念

切面(类)

连接点(所有可能触发切点的点)

切点 (方法)

通知 (方法具体实现代码)

4. Spring AOP 的实现

1.  添加SpringBoot AOP 框架支持

2. 定义切面和切点(设置拦截规则)

2.1 Aspect 语法中的通配符

2.2 AspectJ 语法(Spring AOP 切点的匹配方法)

3. 定义通知

4. 创建连接点 (所有触发拦截方法的点)

5. Spring AOP 的实现原理

织入(Weaving)

JDK动态代理与CGLIB的区别:



1. 什么是Spring AOP?

想要知道Spring AOP,就得先了解AOP

    AOP是面向切面编程,是一种思想,是对某一类事情的集中处理,其核心思想是将那些与业务逻辑无关,但是被多处业务逻辑模块共享的代码(比如日志管理,权限检查,事务管理等)抽取出来,通过预编译方式和运行期动态代理实现程序功能的统一维护的方式。这样,开发者可以将更多的精力放在处理核心业务逻辑上。

AOP是一种思想,Spring AOP是一种具体实现的框架(就类似 Spring IoC 和DI 的关系一样.

2. 为什么要使用AOP?

对一些功能统一,使用较多,我们就可以考虑使用AOP思想进行统一处理,如登录校验,使得我们不用在每一处需要做登录校验的地方进行相同逻辑的代码实现了(下面详细解释)

  • 在一个应用程序中,可能有很多操作都需要在执行前验证用户是否已经登录。如果不使用AOP,你可能需要在每个需要验证的方法中都写一段校验代码。这不仅使得代码重复,而且如果以后需要更改验证逻辑,就需要去修改每一个方法中的代码。
  • 但是,如果使用了AOP,你就可以将校验逻辑抽取出来定义成一个切面,然后通过配置,让这个切面在需要验证的方法执行前运行。这样,你就只需要在一个地方编写和维护验证逻辑,大大提高了代码的可维护性和可读性。

除了登录校验,AOP还可以用在这些地方:

  •     统一日志记录
  •     统一方法执行时间统计
  •     统一返回格式
  •     统一异常处理
  •     事务开启和提交

3. AOP相关组成的概念

切面(类)

指的是某一类事情的具体内容,比如用户登录校验就是一个切面,日志统一记录也是一个切面,切面由切点和通知组成,通常切面是一个

  • 切⾯是包含了:通知、切点和切⾯的类,相当于 AOP 实现的某个功能的集合
  • 可以把切面看作是一个模块,它的目标是完成一些特定的工作,这些工作通过通知实现,而切点则确定了这些工作应当在何处执行。

连接点(所有可能触发切点的点)

所有可能触发 切点的点(拦截方法的点)就称之为连接点

连接点是程序执行的某个特定位置,如类的某个方法调用前、调用后、方法捕获到异常后等。在Spring AOP中,一个连接点总是代表一个方法的执行

切点 (方法)

切点用来定义AOP拦截的规则的(如登录拦截规则),通常是类中的一个方法(该方法没有实现)

  • 切点实际上定义了你的通知应该在何处执行。这个"何处"就是由满足切点规则的那些连接点组成的集合。
  • 你可以将切点视为一个包含了多个连接点的集合,这个集合中的每个元素(即每个连接点)都将应用通知(执行通知中的方法)

通知 (方法具体实现代码)

"通知"是一个重要的概念,它是具体的业务逻辑,定义了何时(触发的条件)和如何(执行的操作)进行拦截。 

  • 切⾯也是有⽬标的 ——它必须完成的⼯作。在 AOP 术语中,切⾯的⼯作被称之为通知
  • Spring 切⾯类中,可以在⽅法上使⽤以下注解,会设置⽅法为通知⽅法,在满⾜条件后会通知本⽅法进⾏调⽤
  1. 前置通知@Before:这个注解标注的方法会在目标方法(实际要执行的方法)被调用前执行
  2. 后置通知@After:这个注解标注的方法会在目标方法完成后执行,无论目标方法是否成功完成。
  3. 环绕通知@Around:这个注解标注的方法会在目标方法调用前后都执行,可以自行决定何时执行目标方法。
  4. 异常通知@AfterThrowing:这个注解标注的方法会在目标方法抛出异常后执行。
  5. 方法返回通知@AfterReturning:这个注解标注的方法会在目标方法成功返回后执行

举个例子,假设你有一个服务类,包含了一个login方法,你希望在这个方法执行前进行日志记录,那么login方法就是你的"目标方法"。你可以定义一个切点来匹配这个方法,然后通过一个前置通知(@Before)来在这个方法执行前记录日志。

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

4. Spring AOP 的实现

1. 添加 SpringBoot AOP 框架支持.➡️ 中央仓库链接
2. 定义切面和切点.
3. 定义通知.

1.  添加SpringBoot AOP 框架支持

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.7.11</version>
</dependency>

2. 定义切面和切点(设置拦截规则)

  • 其中 pointcut⽅法为空⽅法,它不需要有⽅法体,此⽅法名就是起到⼀个“标识”的作⽤,标识下⾯的通知⽅法具体指的是哪个切点(因为切点可能有很多个)
@Aspect //标识当前类为一个切面
@Component //不能省略
public class UserAop {
    // 定义一个切点(设置拦截规则)
    @Pointcut("execution(* com.example.demo.common.controller.UserController.*(..))")
    public void pointcut(){
        
    }
}

2.1 Aspect 语法中的通配符

  •     * : 表示匹配任意的内容,用在返回值,包名,类名,方法都可以使用
  •     .. : 匹配任意字符,可以使用在方法参数上,如果用在类上需要配合 * 一起使用
  •     + : 表示匹配指定类及其它底下的所有子类,比如 com.Car+ 表示匹配 Car 及其所有子类

2.2 AspectJ 语法(Spring AOP 切点的匹配方法)

切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法为:

execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)

其中修饰符和异常可以省略,下面是具体含义:

  • 修饰符(一般省略):public(公共方法),*(任意)
  • 返回类型(不能省略):void,String,int,*(任意)
  • 包:com.demo(固定包),com.*(com包下所有),com.demo..(com.demo包下所有子包含自己)
  • 类:Test(固定类),Test*(以之开头),*test(以之结尾),*(任意)
  • 方法名(不能省略):addUser(固定方法),add*(以add开头),*add(以add结尾),*(任意)
  • 参数:(),(int),(int,String),(..)任意参数
  • 异常(可省略,一般不写)

3. 定义通知

  • 切点,通知都在 UserAop 这个类中.
// 前置通知
    @Before("pointcut()")
    public void doBefore() {
        System.out.println("执行了前置通知 " + LocalDateTime.now());
    }

    // 后置通知
    @After("pointcut()")
    public void doAfter() {
        System.out.println("执行了后置通知 " + LocalDateTime.now());
    }

    // return 之前通知
    @AfterReturning("pointcut()")
    public void doAfterReturning() {
        System.out.println("执行了返回之后通知 " + LocalDateTime.now());
    }

    // 抛出异常之前通知
    @AfterThrowing("pointcut()")
    public void doAfterThrowing() {
        System.out.println("执行了返回之后通知 " + LocalDateTime.now());
    }

    // 环绕通知
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {  // 拿到目标方法的执行对象
        // 这个对象是框架能否继续执行后续流程的对象, 与目标方法是否返回值, 以及返回类型无关
        Object res = null;
        // 前置业务代码
        System.out.println("执行了环绕通知的前置方法 " + LocalDateTime.now());
        try {
            // 执行目标方法
            res = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        // 后置业务代码
        System.out.println("执行了环绕通知的后置方法 " + LocalDateTime.now());
        return res;
    }

4. 创建连接点 (所有触发拦截方法的点)

//创建连接点
@RestController
public class UserController {

    @RequestMapping("/sayHi")
    public String sayHi() {
        System.out.println("执行了 sayHi 方法");
        return "hi, spring boot aop.";
    }
}

【分析】 

  • 切点的拦截规则表示,拦截 UserController 类里面的所有方法. (后面细讲拦截规则)
  • 切点里的 pointcut() 方法是为了给后面的通知使用该方法名.
  • 前四种通知都很简单, 除了注解不一样, 其他都一样. 主要是环绕通知, 它带有参数, 参数 joinPoint 的意义就是拿到目标方法中的执行对象, 也就是 UserController 中的所有方法的执行对象. 用这个对象调用 proceed() 就是执行 UserController 中的所有方法. 环绕通知的返回值 res , 和目标方法的方法类型无关, 它只决定框架能否继续执行后续流程. 
  • ProceedingJoinPoint 接口定义了以下方法:
  1. Object[] getArgs():获取目标方法的参数数组。
  2. Signature getSignature():获取目标方法的签名对象。
  3. Object getTarget():获取目标对象。
  4. Object proceed() throws Throwable:调用目标方法,并返回方法的返回值。如果目标方法抛出异常,则抛出该异常。
  5. Object getThis():获取当前对象,即 AOP 框架生成的代理对象。

通过 ProceedingJoinPoint 接口,我们可以自由地控制目标方法的执行,实现对目标方法的增强、修改或拦截。

【测试】

  • 现在我们测试一下我们的 AOP 是否可以拦截 UserController 中的方法.
  • 当我们通过浏览器访问 sayHi() 方法时, 观察控制台的信息:

【结论】

  1. 成功拦截了 UserController 中的方法.
  2. 环绕通知的前置方法在最前面执行, 环绕通知的后置方法在最后执行.
  3. 通过上面 AOP的简单实现, 我们大概也就知道了如何对 "用户登录效验功能进行统一处理了. 只需要在同一将所有调用用户登录校验的方法写在一个类中, 或者一个文件夹下, 然后设置对应的拦截规则即可. 

如果想进一步验证 "拦截规则" 是否正确, 可以在 controller 包下再建一个 TestController 类, 然后写一个方法, 并通过浏览器访问, 观察是否还打印了这几个通知方法. (答案肯定是没有, 下来可以自己试一下)

5. Spring AOP 的实现原理

由于Spring AOP 的实现建在动态代理基础上的,  Spring 对 AOP 的支持局限于方法级别的拦截.

动态代理呢就是当调用者调用目标对象的时候,它不会与目标对象接触,而是由代理类进行调用

织入(Weaving)

简单的来说就是什么时候生成代理对象
织入就是把切面应用到目标对象中并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中,一共有以下三种织入时期:

  •     编译期间:切面在类编译时被织入
  •     类加载期间:切面在类被加载到jvm时被织入
  •     运行期:切面在程序运行的某一时刻被织入,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的

Spring AOP支持JDK Proxy和CGLIB方式实现动态代理,这两种方式都是在程序运行期,动态的将切面织入字节码形成代理对象

JDK动态代理与CGLIB的区别:

  • JDK动态代理要求被代理的类必须实现接口,因此它只能代理接口中定义的方法,当你通过代理对象调用接口中的方法时,这个调用会被转发到一个InvocationHandler,然后通常会通过反射来调用被代理对象的原始方法. CGLIB动态代理不要求被代理的类实现接口,而是通过继承被代理类
  • JDK动态代理性能相对较高,生成代理对象速度较快,而CGLIB动态代理性能相对较低,生成代理对象速度较慢
  •  CGLIB动态代理无法代理final类和final方法,而JDK动态代理可以代理任意类的方法

 综上所述,JDK Proxy 和 CGLIB 都有自己的优缺点和适用场景。如果目标对象实现了接口并且需要代理的方法较少,则建议使用 JDK Proxy;如果目标对象没有实现接口或需要代理的方法较多,则建议使用 CGLIB。

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

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

相关文章

NFC type 12345 tag介绍

NFC(近场通信)被称为短距离无线技术&#xff0c;是一套通信协议&#xff0c;NFC技术将非接读卡器/Reader、非接标签/Tag和点对点(Peer-to-Peer)数据交换的功能设计融为一体!使电子设备之间能够进行简单、安全的双向交互。为推动NFC技术发展&#xff0c;2004年&#xff0c;诺基亚…

Stable-Diffusion环境搭建

硬件可以采用DELL R7525 搭配L4 或者T4 等等企业级显卡 环境如下&#xff1a; 可以看到有相应的GPU卡信息 esxi 7.u3 信息 设置GPU穿透方式 查看相应的虚拟机参数信息 PCI 设备加载穿透GPU信息 启动uefi 设置相应的参数信息 https://docs.nvidia.com/grid/latest/grid-vgpu-re…

如何检测视频中的绿屏、绿帧问题

今天给项目拷机&#xff0c;发现视频会偶现绿屏&#xff0c;非常偶现&#xff0c;很难复现出来。 由于问题暂时没有定位&#xff0c;只能先表面解决一下&#xff0c;就是过滤掉出现绿屏的帧。 当然&#xff0c;首先要把绿帧检测出来&#xff0c;才能做后续的补救措施。 绿屏、…

电感公式推导

目录 电感的磁感应强度用&#xff1a;B表示 加入磁芯的可以提高磁感应强度&#xff1a;BμNI &#xff08;μ > μ0&#xff09; 磁芯的磁通量用&#xff1a;Φ来表示 一匝线圈感生电动势用&#xff1a;E来表示 在整个电感线圈的里面产生的感生电动势用UL来表示&#xff…

软件测试项目实战,电商项目核心业务测试分析(全覆盖)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 登陆功能怎么测试…

第16章_多版本并发控制

第16章_多版本并发控制 1. 什么是MVCC MVCC(Multiversion Concurrency Control)&#xff0c;多版本并发控制。顾名思义&#xff0c;MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之&#xff0…

chapter9: SpringBoot自定义Starter

尚硅谷SpringBoot顶尖教程 1. 自定义starter介绍 自定义starter从下面两个方面着手&#xff1a; 这个自定义starter的场景需要用到哪些依赖&#xff1f;如何编写自定义starter的自动配置&#xff1f; 查看springboot提供的已有starter组件的自动配置类&#xff0c;基本使用…

NFC Forum Type2 Tag

RC522作为一款NFC读写芯片&#xff0c;性价比还是很高的&#xff0c;因为在项目里需要采用NFC OOB配对&#xff0c;所以需要读取配对方模拟的NFC卡片信息 读取对象采用NRF52832&#xff0c;使用其NFC功能模拟type2 tag&#xff0c;但是读取方式和M1卡不一样&#xff0c;踩了不…

软件测试员不要过于迷信技术,忽视软技能

精于技术对于测试员&#xff0c;乃至技术员本身没毛病&#xff0c;甚至应大加赞赏&#xff0c;在组织中更应像国宝熊猫一样照顾好。然而我们发现&#xff0c;一些精于技术的测试员混的并不好。“纯正”的技术人员就该吃亏&#xff1f;问题到底出现在哪里&#xff1f; 根据我对…

南大通用GBase 8c 多模多态分布式数据库系列二之安装与卸载

目录 一.前言 二. 学习目标 三. 安装流程 四. 配置要求 1. 硬件配置要求 2. 软件配置要求 3. 软件依赖配置 五. 集群规划 1. 物理规划 2. 演示环境配置 六. 安装前环境检查 1. 关闭防火墙 2. 关闭SELINUX 3. 主机名检查、依赖检查 4. Gbase用户配置sudo 七. 配…

科研闭环指南|关于 Review Rebuttal 的二三事

两个月前投稿的论文审稿&#xff08;Review&#xff09;意见快下来了&#xff0c;期间我也是作为审稿人&#xff08;Reviewer&#xff09;完成了4篇工作的审稿工作。回想自己从入学以来也算是审过 10 篇左右的稿子了&#xff0c;也参与过 Review 之后的 Rebuttal 环节。下面我就…

思维决定发展,软件测试人也不例外

最近特别懒&#xff0c;不想码字&#xff0c;原本写作就很差&#xff0c;更是退化严重。社招和校招面试过很多人&#xff0c;从十年前自己还很弱的时候学着面试&#xff0c;到数百次面试积累之后&#xff0c;面对候选人的时候&#xff0c;我的内心依然有些许紧张&#xff0c;非…

《Reinforcement Learning: An Introduction》第5章笔记

Chapter 5 Monte Carlo Methods Monte Carlo 方法不假设拥有完备的环境知识&#xff0c;它仅仅需要经验–从与环境的实际或模拟交互中得到的一系列的状态、动作、和奖励的样本序列。 Monte Carlo方法是基于平均采样回报的来解决强化学习问题的方法。 5.1 Monte Carlo Predic…

NFS服务器

文章目录 NFS服务器NFS的由来与功能什么是NFS(Network File System)什么是RPC(Remote Procedure Call)NFS启动的RPC daemons NFS Server 端的配置所需要的软件NFS的软件结构/etc/exports配置文件的语法与参数 启动NFSRPC服务的注册状况怎么查看(rpcinfo) NFS的连接查看showmoun…

认识服务器

1、查看操作系统的信息 CentOS 输入&#xff1a;cat /etc/os-release 字段含义解释NAME操作系统名称CentOS LinuxVERSION操作系统版本7 (Core)ID操作系统标识centosID_LIKE相关操作系统标识rhel fedoraVERSION_ID操作系统版本号7PRETTY_NAME可读性较好的操作系统名称CentOS L…

产品如何有效把控产品上线质量

很多人乍看这个标题&#xff0c;可能有些惊讶。产品上线质量不是由测试来主要负责的么&#xff0c;怎么产品也需要来把控这个事情&#xff1f; 诚然&#xff0c;从具体分工而言&#xff0c;产品的线上质量主要由测试来负责&#xff0c;产品按时按量完成由项目项目经理把控。但…

详谈数据中心网络中的四种虚拟化技术:VXLAN、NVGRE、STT和SPBM

概要 在现代的数据中心网络中&#xff0c;虚拟化技术被广泛应用以提供更高的可扩展性、灵活性和效率。数据中心网络虚拟化允许多个虚拟网络在共享的物理基础设施上运行&#xff0c;使得网络资源的管理更为简单和高效。本文将详细介绍数据中心网络中的四种不同类型的虚拟化技术&…

全网超全,接口测试用例设计——常见问题和风险,测试不背锅...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口测试用例设计…

【开源工具】使用Whisper提取视频、语音的字幕

这里写目录标题 一、语音转字幕操作步骤1、下载安装包Assets\WhisperDesktop.zip[^2]2、加载模型2.1 下载模型2.1.1 进入Hugging Face[^3]的仓库2.1.2 选择需要下载的模型2.1.3 配置模型路径 3、语音转字幕4、实时语言转录功能 二、相关简介[^1]特点开发人员指南构建说明其他注…

【MySql】表的内连接和外连接

文章目录 内连接外连接左外连接右外连接 OJ题目 本篇博客主要介绍的内容是表的连接&#xff0c;在MySql中表的连接分为内连接和外连接&#xff0c;下面&#xff0c;我们直接进入主题把 内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;我们前…