Spring AOP(AOP概念,组成成分,实现,原理)

目录

1. 什么是Spring AOP?

2. 为什么要用AOP?

3. AOP该怎么学习?

3.1 AOP的组成

(1)切面(Aspect)

(2)连接点(join point)

(3)切点(Pointcut)

(4)通知(Advice)

 4. Spring AOP实现

4.1 添加 AOP 框架支持

​编辑

 4.2 定义切面

4.3 定义切点

4.4 定义通知

4.5 切点表达式说明 AspectJ

5.使用 AOP 统计 UserController 每个方法的执行时间 StopWatch

6. Spring AOP 实现原理

6.1 生成代理的时机 :织入(Weaving)

6.2 JDK 动态代理实现

6.3 CGLIB 动态代理实现

6.4 JDK 和 CGLIB 实现的区别(面试常问)


1. 什么是Spring AOP?

AOP(Aspect Oriented Programming):面向切面编程,它和 OOP(面向对象编程)类似。

面向切面编程就是面对某一方面、某个问题做集中的处理

针对某一类事情进行集中处理,这一类事情就是切面

比如用户登录权限的效验,在学习 AOP 之前,在需要判断用户登录的页面,都要各自实现或调用用户验证的方法,学习 AOP 之后,我们只需要在某一处配置一下,那么所有需要判断用户登录的页面就全部可以实现用户登录验证了,不用在每个方法中都写用户登录验证了

 AOP 是一种思想,而 Spring AOP 是实现(框架),这种关系和 IOC(思想)与 DI(实现)类似
 

 

2. 为什么要用AOP?

  • 采用AOP,我们可以不修改源代码,添加新的功能。我们单独编写独立的权限判断模块,并通过配置,将其配置到登录流程中(比如用户登录验证等)
  • 更加便捷,想象我们做一个类似CSDN这类博客系统,在之前我们除了登录、注册不需要验证用户是否登录,其余所有页面几乎都要验证,且在每个功能的代码都需要再写一遍,这就显得十分麻烦,对于这类功能统一的,且地方使用较多的功能,AOP会表现的更加出色

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

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

也就是说使用AOP可以扩充多个对象的某个能力,所以AOP可以说是OOP(Object OrientedProgramming,面向对象编程)的补充和完善。

3. AOP该怎么学习?

  1. 学习 AOP 是如何组成
  2. 学习 Spring AOP 的使用
  3. 学习 Spring AOP 实现原理

3.1 AOP的组成

(1)切面(Aspect)

定义 AOP 是针对某个统一的功能的,这个功能就叫做一个切面,比如用户登录功能或方法的统计日志,他们就各是一个切面。切面是由切点和通知组成的。

通俗的理解就是,切面就是处理某一个具体问题的一个类,类中包含了很多方法,这些方法就是切点和通知

(2)连接点(join point)

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

(3)切点(Pointcut)

切点的作用就是提供一组规则来匹配连接点,给满足规则的连接点添加通知,总的来说就是,定义 AOP 拦截的规则的

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

用来进行主动拦截的规则(配置)

(4)通知(Advice)

拦截到这个行为后要做什么事就是通知

 Spring 切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后悔通知本方法进行调用:

  • 前置通知使用@Before:通知方法会在目标方法调用之前执行
  • 后置通知使用@After:通知方法会在目标方法调用之后执行
  • 返回之后通知使用@AfterReturning通知方法会在目标方法返回后调用
  • 抛异常后通知@AfterThrowing:通知方法会在目标方法爬出异常之后调用
  • 环绕通知:@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为

就相当于在一个生产型公司中

通知相当于底层的执行者,切点是小领导制定规则,切面是大领导制定公司的发展方向,连接点是属于一个普通的消费者用户

以CSDN的登录为例子:

 4. Spring AOP实现

Spring AOP 实现步骤

  1.     添加 Spring AOP 框架支持
  2.     定义切面和切点
  3.     实现通知

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

4.1 添加 AOP 框架支持

创建Spring Boot项目时是没有Spring AOP框架可以选择的,这个没关系,咱们创建好项目之后,再在pom. xml中添加Spring AOP的依赖即可。

 4.2 定义切面

@Aspect // 告诉框架我是一个切面类
@Component // 随着框架的启动而启动
public class UserAspect {
}

4.3 定义切点

@Aspect // 告诉框架我是一个切面类
@Component // 随着框架的启动而启动
public class UserAspect {/*** 切点(配置拦截规则)*/@Pointcut("execution(* com.example.demo.Controller.*.*(..))")public void pointcut() {}
}

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

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

4.4 定义通知

@Aspect // 告诉框架我是一个切面类
@Component // 随着框架的启动而启动
public class UserAspect {/*** 切点(配置拦截规则)*/@Pointcut("execution(* com.example.demo.Controller.*.*(..))")public void pointcut() {}/*** 前置通知*/@Before("pointcut()")public void beforeAdvice() {System.out.println("执行了前置通知~");}/*** 后置通知*/@After("pointcut()")public void afterAdvice() {System.out.println("执行了后置通知~");}/*** 环绕通知*/@Around("pointcut()")public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){Object obj = null;System.out.println("进入环绕通知之前");// 执行目标方法try {obj = proceedingJoinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("退出环绕通知了");return obj;}
}

UserController实体类:

package com.example.demo.Controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Created with IntelliJ IEDA.* Description:* User:86186* Date:2023-08-10* Time:16:43*/
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/hi")public String sayHi(String name) {System.out.println("执行了 sayHi 方法");return "Hi," + name;}@RequestMapping("/hello")public String sayHello() {System.out.println("执行了 sayHello 方法");return "Hello, world.";}}

ArticleController实体类:

package com.example.demo.Controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Created with IntelliJ IEDA.* Description:* User:86186* Date:2023-08-10* Time:16:45*/@RestController
@RequestMapping("/art")
public class ArticleController {@RequestMapping("/hi")public String sayHi() {System.out.println("文章的 sayHI~");return "Hi, world.";}}

当浏览art/hi时:

 

此时控制台只有articleControlle中的打印,没有前置、后置通知

但是当我们去访问

再次刷新

4.5 切点表达式说明 AspectJ

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

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

 

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

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

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

AspectJ ⽀持三种通配符

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

修饰符,一般省略

  •     public 公共方法
  •     *任意

返回值,不能省略

  •     void 返回没有值
  •     String 返回值字符串
  •     *任意

包,通常不省略,但可以省略

  •     com.gyf.crm 固定包
  •     com.gyf.crm.*.service crm 包下面子包任意(例如:com.gyf.crm.staff.service)
  •     com.gyf.crm… crm 包下面的所有子包(含自己)
  •     com.gyf.crm.*service… crm 包下面任意子包,固定目录 service,service 目录任意包

类,通常不省略,但可以省略

  • UserServiceImpl 指定类
  • *Impl 以 Impl 结尾
  • User* 以 User 开头
  • *任意

方法名,不能省略

  • addUser 固定方法
  • add* 以 add 开头
  • *DO 以 DO 结尾
  • *任意

参数

  • () 无参
  • (int) 一个整形
  • (int,int)两个整型
  • (…) 参数任意

throws可省略,一般不写

表达式示例

  • 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

5.使用 AOP 统计 UserController 每个方法的执行时间 StopWatch

 Spring AOP 中统计时间用 StopWatch 对象:

    // 添加环绕通知@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) {// spring 中的时间统计对象StopWatch stopWatch = new StopWatch();Object result = null;try {stopWatch.start(); // 统计方法的执行时间,开始计时// 执行目标方法,以及目标方法所对应的相应通知result = joinPoint.proceed();stopWatch.stop(); // 统计方法的执行时间,停止计时} catch (Throwable e) {e.printStackTrace();}System.out.println(joinPoint.getSignature().getDeclaringTypeName() + "." +joinPoint.getSignature().getName() +"执行花费的时间:" + stopWatch.getTotalTimeMillis() + "ms");return result;}

 

 

6. Spring AOP 实现原理

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

 

Spring AOP 动态代理实现:

默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类

  •     JDK Proxy(JDK 动态代理)
  •     CGLIB Proxy:默认情况下 Spring AOP 都会采用 CGLIB 来实现动态代理,因为效率高
  •     CGLIB 实现原理:通过继承代理对象来实现动态代理的(子类拥有父类的所有功能)
  •     CGLIB 缺点:不能代理最终类(也就是被 final 修饰的类)
     

6.1 生成代理的时机 :织入(Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中

在目标对象的生命周期中有多个点可以进行织入

  1. 编译期:切面在目标类编译时被织入,这种方法需要特殊的编译器,AspectJ 的织入编译器就是以这种方式织入切面的
  2. 类加载期:切面在目标类加载到 JVM 时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码,AspectJ5 的加载时织入 (load-time weaving. LTW)就支持以这种方式织入切面
  3. 运行期:切面在应用运行的某一时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP 就是以这种方式织入切面的
     

6.2 JDK 动态代理实现

JDK 动态代理就是依靠反射来实现的

//动态代理:使⽤JDK提供的api(InvocationHandler、Proxy实现),此种⽅式实现,要求被
代理类必须实现接⼝
public class PayServiceJDKInvocationHandler implements InvocationHandler {//⽬标对象即就是被代理对象private Object target;public PayServiceJDKInvocationHandler( Object target) {this.target = target;}//proxy代理对象@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//1.安全检查System.out.println("安全检查");//2.记录⽇志System.out.println("记录⽇志");//3.时间统计开始System.out.println("记录开始时间");//通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args);//4.时间统计结束System.out.println("记录结束时间");return retVal;}public static void main(String[] args) {PayService target= new AliPayService();//⽅法调⽤处理器InvocationHandler handler = new PayServiceJDKInvocationHandler(target);//创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建PayService proxy = (PayService) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{PayService.class},handler);proxy.pay();}
}

6.3 CGLIB 动态代理实现

public class PayServiceCGLIBInterceptor implements MethodInterceptor {//被代理对象private Object target;public PayServiceCGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxymethodProxy)throws Throwable {//1.安全检查System.out.println("安全检查");//2.记录⽇志System.out.println("记录⽇志");//3.时间统计开始System.out.println("记录开始时间");//通过cglib的代理⽅法调⽤Object retVal = methodProxy.invoke(target, args);//4.时间统计结束System.out.println("记录结束时间");return retVal;}public static void main(String[] args) {PayService target= new AliPayService();PayService proxy= (PayService) Enhancer.create(target.getClass(),new PayServiceCGLIBInterceptor(target));proxy.pay();}
}

6.4 JDK 和 CGLIB 实现的区别(面试常问)

  1. JDK 实现,要求被代理类必须实现接口,之后是通过 InvocationHander 及 Proxy,在运行时动态的在内存中生成了代理对象,该代理对象是通过实现同样的接口实现(类似静态代理接口实现的方式),只是该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成的
  2. CGLIB 实现,被代理类可以不实现接口,是通过继承被代理类,在运行时动态的生成代理类对象,这种方式实现方式效率高

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringMVC概述、SpringMVC的工作流程、创建SpringMVC的项目

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 Spring MVC入门 一、Spring MVC概述二、入门案例2.1导入Sp…

【go语言学习笔记】04 Go 语言工程管理

文章目录 一、质量保证1. 单元测试1.1 定义1.2 Go 语言的单元测试1.3 单元测试覆盖率 2. 基准测试2.1 定义2.2 Go 语言的基准测试2.3 计时方法2.4 内存统计2.5 并发基准测试2.6 基准测试实战 3. 特别注意 二、性能优化1. 代码规范检查1.1 定义1.2 golangci-lint1.2.1 安装1.2.2…

【C++】STL——stack和queue的模拟实现、空间适配器、deque的介绍、增删查改函数的简单实现

文章目录 1.deque的简单介绍2.模拟实现stack3.模拟实现queue 1.deque的简单介绍 deque的介绍文档 deque(双端队列)&#xff1a;是一种双开口的"连续"空间的数据结构&#xff0c;双开口的含义是&#xff1a;可以在头尾两端进行插入和删除操作&#xff0c;且时间复杂度…

好的测试数据管理,到底要怎么做?

你的组织是否实施了测试数据管理&#xff1f;如果你的组织处理关键或敏感的业务数据&#xff0c;测试数据管理肯定会让组织受益。与测试数据相关的问题占所有软件缺陷的 15%&#xff0c;这一事实强调了测试数据的重要性。本文将准确讨论测试数据经理职责、测试数据经理需要什么…

Unity Shader:闪烁

还是一样的分为UI闪烁和物体闪烁&#xff0c;其中具体可分为&#xff1a;UI闪烁、物体闪烁与半透明闪烁 1&#xff0c;UI闪烁 对于UI 还是一样的&#xff0c;改写UI本身的shader&#xff1a; Shader "UI/YydUIShanShder" {Properties{[PerRendererData] _MainTex(…

UML类图

UML类图 类与类之间的关系 类与类之间的关系 依赖 一个类的对象,作为另一个类的局部变量, 虚线加箭头表示继承 实线三角实现 虚线三角关联 一个类的对象,作为一个类的字段 实线箭头 a. 组合 实心菱形实线箭头 b. 聚合 空心菱形实线箭头

vue3 - 使用reactive定义响应式数据进行列表赋值时,视图没有更新的解决方案

文章目录 1&#xff0c;问题2&#xff0c;原因3&#xff0c;解决方案一、再封装一层数据&#xff0c;即定义属性名&#xff0c;在后期赋值的时候&#xff0c;对此属性进行直接赋值三、使用数组的splice来直接更改原数组三、使用 ref 来定义数据 1&#xff0c;问题 在Vue 3.0 中…

快速入门:【c# 之 Winform开发】

C#基础 面向对象(OOP) c语言是面向过程。 c是面向过程面向对象。 c#是纯粹的面向对象: 核心思想是以人的思维习惯来分析和解决问题。万物皆对象。 面向对象开发步骤: 分析对象 特征行为关系(对象关系/类关系) 写代码: 特征–>成员变量 方法–>成员方法 实例化–具体对…

gitblit-使用

1.登入GitBlit服务器 默认用户和密码: admin/admin 2.创建一个新的版本库 点击图中的“版本库”&#xff0c;然后点击图中“创建版本库” 填写名称和描述&#xff0c;注意名称最后一定要加 .git选择限制查看、克隆和推送勾选“加入README”和“加入.gitignore文件”在图中的1处…

【C++】多态的底层原理(虚函数表)

文章目录 前言一、虚函数表二、派生类中虚函数表1.原理2.例子&#xff1a; 三、虚函数的存放位置四 、单继承中的虚函数表五、多继承中的虚函数表六、问答题 前言 一、虚函数表 通过观察测试我们发现b对象是8bytes&#xff0c;除了_b成员&#xff0c;还多一个__vfptr放在对象的…

FFmpeg安装和使用

sudo apt install ffmpeg sudo apt-get install libavfilter-devcmakelist模板 CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(ffmpeg_demo)# 设置ffmpeg依赖库及头文件所在目录&#xff0c;并存进指定变量 set(ffmpeg_libs_DIR /usr/lib/x86_64-linux-gnu) …

Redis类型检查与命令多态

Redis中用于操作键的命令基本上可以分为两种类型。 其中一种命令可以对任何类型的键执行&#xff0c;比如说DEL命令、EXPIRE命令 、RENAME命令、TYPE命令、OBJECT命令等。 举个例子&#xff0c;以下代码就展示了使用DEL命令来删除三种不同类型的键: # 字符串键 redis> SE…

【安装部署】Mysql下载及其安装的详细步骤

1.下载压缩包 官网地址&#xff1a;www.mysql.com 2.环境配置 1.先解压压缩包 2.配置环境变量 添加环境变量&#xff1a;我的电脑--->属性-->高级-->环境变量-->系统变量-->path 3.在mysql安装目录下新建my.ini文件并&#xff0c;编辑my.ini文件 编辑内容如…

Tomcat 部署及优化

Tomcat概述 Tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器&#xff0c;是 Apache 软件基金会的 Jakarta 项目中的一个核心项目&#xff0c;由 Apache、Sun 和其他一些公司及个人共同开发而成。在中小型系统和并发访问用户不是很…

java版工程项目管理系统源码+系统管理+系统设置+项目管理+合同管理+二次开发em

​ 鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部…

帕累托森林:IEEE Fellow唐远炎院士出任「儒特科技」首席架构官

导语 「儒特科技」作为一家拥有全球独创性极致化微内核Web引擎架构的前沿科技企业&#xff0c;从成立即受到中科院软件所和工信部的重点孵化及扶持&#xff0c;成长异常迅速。前不久刚正式官方融入中国五大根操作系统体系&#xff0c;加速为其下游上千家相关衍生OS和应用软件企…

Pytorch迁移学习使用MobileNet v3网络模型进行猫狗预测二分类

目录 1. MobileNet 1.1 MobileNet v1 1.1.1 深度可分离卷积 1.1.2 宽度和分辨率调整 1.2 MobileNet v2 1.2.1 倒残差模块 1.3 MobileNet v3 1.3.1 MobieNet V3 Block 1.3.2 MobileNet V3-Large网络结构 1.3.3 MobileNet V3预测猫狗二分类问题 送书活动 1. MobileNet …

·[K8S:使用calico网络插件]:解决集群节点NotReady问题

文章目录 一&#xff1a;安装calico&#xff1a;1.1&#xff1a;weget安装Colico网络通信插件&#xff1a;1.2&#xff1a;修改calico.yaml网卡相关配置&#xff1a;1.2.1&#xff1a;查看本机ip 网卡相关信息&#xff1a;1.2.2&#xff1a;修改calico.yaml网卡interface相关信…

arcgis--网络分析(理论篇)

1、定义概念 &#xff08;1&#xff09;网络&#xff1a;由一系列相互联通的点和线组成&#xff0c;用来描述地理要素&#xff08;资源&#xff09;的流动情况。 &#xff08;2&#xff09;网络分析&#xff1a;对地理网络&#xff08;如交通网络、水系网络&#xff09;&…