【Spring AOP学习】AOP的组成 SpringAOP的实现和实现原理

目录

一、认识SpringAOP

1、AOP是什么?

2、AOP的功能

3、AOP的组成(重要)

二、SpringAOP的实现

🌷1、添加Spring AOP框架支持

🌷2、定义切面和切点

🌷 3、定义通知

3.1 完成代码实现

3.2 具体通知分析

🌷4、小练习:使用AOP统计UserController每个方法的执行时间。

 三、SpringAOP的实现原理(重点)


一、认识SpringAOP

🌷1、AOP是什么?

        AOP是一个思想Spring AOP 是⼀个框架,是对 AOP 思想的实现,它们的关系和IoC 与 DI 类似。OOP是面向对象编程。AOP:面向切面编程:对某一类事情的集中处理(也就是同一类的事务怎么处理)。

栗子:

        比如CSDN:编辑博客,删除博客,写博客都要在用户登录的基础上实现,所以在都实现单个的功能之前,都要对用户登录信息进行验证,各自实现或者调用用户验证的方法,所以非常麻烦。那么抽取成一个公共方法也可以:但是当对公共方法的参数或者其他进行了修改,那么它的所有调用方都要进行修改。所以对于这种功能统⼀,且使⽤的地⽅较多的功能,就可以考虑 AOP来统⼀处理了。所以现在AOP就是:这是哪个功能都要实现的是相同的功能,我们可以将它理解为就是对一类事情的集中处理,现在只需要在某一处配置一下就好了,此时所有需要时判断用户登录的方法就可以全部实现用户登录验证了。

🌷2、AOP的功能

(1)统⼀⽇志记录
(2)统⼀⽅法执⾏时间统计:项目监控、监控项目请求流量、监控接口的响应时间,甚至每个方法的响应时间;
(3)统⼀的返回格式设置:http状态码、code(业务状态码:后端处理响应成功,不代表业务办理成功)、msg(业务处理失败,返回的信息)、data;
(4)统⼀的异常处理;
(5)事务的开启和提交等。


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

 🌷3、AOP的组成(重要)

        比如我们上面举的例子:关于csdn的博客的整个过程就是一个切面:其中:写博客功能、删除博客、编辑博客都是连接点,它们三个都要实现一个用户登录功能的校验,这个用户登录功能就是一个切点,其中用户登录功能中的方法体中实现代码就是一个通知。


二、SpringAOP的实现(重要)

目标:我们就实现上图中的关于csdn的博客的这个AOP的实现。我们使用SpringAOP来实现AOP的功能,目的是拦截所有UserController方法,每次调用UerController中的任何一个方法的时候,都执行相应的通知事件。

SpringAOP的实现步骤如下:

(1)添加SpringAOP的框架支持;

(2)定义切面和切点;

(3)定义通知。

🌷1、添加Spring AOP框架支持

在 pom.xml 中添加如下配置:

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

🌷2、定义切面和切点

//表示是一个切面:CSDN
@Component
@Slf4j
//表示是一个切面:CSDN,要结合五大注解使用
@Aspect
public class LoginAspect {//表示是一个切点:登录功能验证@Pointcut("execution(* aop.controller.UserController.*(..))")public void pointcut1(){//方法体就是通知}
}

🍑总结 :

        切点指的是具体要处理的某一类问题:用户登录权限的验证就是一个具体的问题,也就是一个切点。

(1)切面上要加@Aspect注解,结合五大注解使用:加@Component;

(2)切点: pointcut 方法为空方法,它不需要有方法体,此方法名就是起到⼀个“标识”的作用,标识下面的通知方法具体指的是哪个切点(因为切点可能有很多个)。

(3)一个@Aspect(切面)下可以有多个切点,@Pointcut("execution(* aop.controller.UserController.*(..))")。同时通知方法不是一定要全部存在的。

(4)切点表达式由切点函数组成,其中 execution() 是最常用的切点函数,用来匹配方法,语法为
execution(<修饰符><返回类型><包.类.方法(参数)><异常>)

切点表达式说明:

(1)灰色表示可以省略,红色不能省略;

(2)修饰符是public等,其中*表示任意;

(3)返回值:*表示任意;

(4)包名:

com.example       表示固定包

com.example.*.service   表示example包下任意包下的固定目录service

com.example..     表示example包下所有的子包(含自己)

com.example.*.service..   表示example包下任意子包下固定目录service下的任意包。

(5)类

UserController   表示指定类

*Impl     表示以Impl结尾

User*    表示以User开头

*      表示任意

(6)方法名

writeBlog  固定方法

write*  表示以write开头

*Blog   表示以Blog结尾

* 表示任意

(7)参数

()  表示无参

(int)表示一个整型

(int,int)表示两个整型

(..)表示参数任意

(8)异常:throws一般不写

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


比如:

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。

🌷 3、定义通知

通知定义的是被拦截的方法(具体的写博客、编辑博客和删除博客)具体要执行的业务(用户登录权限的校验)。
使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用:
(1)前置通知使用@Before:通知方法会在目标方法调用之前执行。
(2)后置通知使用@After:通知方法会在目标方法返回或者抛出异常后调用。
(3)返回之后通知使用@AfterReturning:通知方法会在目标方法返回后调用。
(4)抛异常后通知使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。
(5)环绕通知使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。

3.1 完成代码实现

UserController文件

@Slf4j
@RequestMapping("/aop")
@RestController
public class UserController {//模拟写博客@RequestMapping("/writeBlog")public String writeBlog(){//模拟异常
//        int a = 10/0;log.info("write blog success...");return "write blog success...";}//编辑博客@RequestMapping("/editBlog")public String editBlog(){log.info("edit blog success...");return "edit blog success...";}//删除博客@RequestMapping("/deleteBlog")public String deleteBlog(){log.info("deleteBlog success...");return "deleteBlog success...";}
}

 LoginAspect文件


@Component
@Slf4j
//表示是一个切面:CSDN,要结合五大注解使用
@Aspect
public class LoginAspect {//表示是一个切点:登录功能验证@Pointcut("execution(* aop.controller.UserController.*(..))")public void pointcut1(){ }//1、前置通知使用@Before:通知方法会在目标方法调用之前执行。@Before("pointcut1()")public void doBefore(){log.info("do before...");}//2、后置函数@After@After("pointcut1()")public void doAfter(){log.info("do After...");}//3、@AfterReturning 在return 之前通知@AfterReturning("pointcut1()")public void doAfterReturning(){log.info("do doAfterReturning...");}//4、@doAfterThrowing 在return 之前通知@AfterThrowing("pointcut1()")public void doAfterThrowing(){log.info("do doAfterThrowing...");}//5、@doAround 环绕方法@Around("pointcut1()")//环绕方法要写返回结果  //ProceedingJoinPoint joinPoint表示连接点public Object doAround(ProceedingJoinPoint joinPoint){Object object = null;log.info("环绕通知执行之前执行的方法...");//执行目标方法try {object = joinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}log.info("环绕通知执行之后执行的方法...");return object;}@Pointcut("execution(* aop.controller.UserController1.*(..))")public void pointcut2(){ }
}

3.2 具体通知分析

(1)@Before

@Before在具体的目标方法调用之前执行。

测试UserController类中的三个方法都成功:比如 

 2、@After

@After在具体调用目标方法之后执行;

(3)@AfterReturing

使用@AfterReturing会在目标方法return之前通知,知道@AfterReturing在@After之前执行;

(4)@AfterThrowing

🍑总结

         @doAfterThrowing只有在异常的时候才会执行,正常返回不会执行。异常情况@doAfterThrowing执行之后,@doAfterReturning就不会执行了。正常返回的时候执行@doAfterReturning,出现异常就不会执行了。

正常情况:

 异常情况:

(5)@Around(最常用)

🍑注意:

        先执行环绕通知,再执行前置通知,接着是目标方法,然后是doAfterReturing和do After后置通知。

  • getSignature()) //获取修饰符+ 包名+组件名(类名) +方法名
  • getSignature().getName()) //获取方法名
  • getSignature().getDeclaringTypeName()) //获取包名+组件名(类名)

🌷4、小练习:使用AOP统计UserController每个方法的执行时间。

普通写法:

在UserController中的每个方法都按如下计算:缺点是所有的方法(包括writeBlog,editBlog方法)都要写一下,冗余性很高。

AOP的写法

 /*** 计算UserController中所有方法的Log的执行时间*///表示是一个切点:登录功能验证@Pointcut("execution(* aop.controller.UserController.*(..))")public void pointcut2(){ }//@doAround 环绕方法@Around("pointcut2()")//环绕方法要写返回结果public Object doAround(ProceedingJoinPoint joinPoint){Object object = null;long start = System.currentTimeMillis();//执行目标方法try {object = joinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}//getSignature()) //获取修饰符+ 包名+组件名(类名) +方法名log.info(joinPoint.getSignature().toLongString()+"耗时"+(System.currentTimeMillis()-start));return object;}

测试结果:

 时间不同主要是cpu调度的偏差。


 三、SpringAOP的实现原理(重点)


简单了解代理模式~https://mp.csdn.net/mp_blog/creation/editor/131963215

🍑总结:

(1)SpringAOP是构建在动态代理的基础上,所以Spring对AOP的支持局限于方法级别的拦截。

(2)Spring AOP支持JDK Proxy和 CGLIB的方式实现动态代理;

(3)proxyTargetClass为false,目标实现了接口,AOP默认会基于JDK的方式生成代理类;

(4)proxyTargetClass为false,没有实现接口的类,AOP会基于CGLIB的方式生成代理类;

(5)proxyTargetClass为false为true,AOP会基于CGLIB的方式生成代理类。


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

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

相关文章

抽象工厂模式——产品族的创建

1、简介 1.1、简介 抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比&#xff0c;抽象工厂模式中的具体工厂不只是创建一种产品&#xff0c;它负责创建一族产品 1.2、定义 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;&#xff1a;提供…

Acwing.898 数字三角形(动态规划)

题目 给定一个如下图所示的数字三角形&#xff0c;从顶部出发&#xff0c;在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点&#xff0c;一直走到底层&#xff0c;要求找出─条路径&#xff0c;使路径上的数字的和最大。 输入格式 第一行包含整数n&#xff0…

螺旋矩阵 II

给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 示例 2&#xff1a; 输入&#xff1a;n 1 输出&a…

零信任网络架构与实现技术的研究与思考

目前&#xff0c;国外已有较多有关零信任网络的研究与实践&#xff0c;包括谷歌的 BeyondCorp、BeyondProd&#xff0c;软件定义边界&#xff08;Software Defined Perimeter&#xff0c;SDP&#xff09; 及盖特提出的“持续自适应风险与信任评估”等。国内也有不少安全厂商积极…

vue中预览静态pdf文件

方法 // pdf预览 viewFileCompare() { const pdfUrl "/static/wjbd.pdf"; window.open(pdfUrl); }, // 下载 downloadFile(){ var a document.createElement("a"); a.href "/static/wjbd.pdf"; a.…

关于Anaconda环境配置的一些问题

文章目录 一、关于package文件安装位置二、关于尝试下载Python包时出现的CondaSSLError三、配置环境的整个流程 一、关于package文件安装位置 package 文件安装在envs目录底下的Lib中&#xff0c;可以参考一下。 在对应的Python脚本文件中&#xff0c;选择Parameters&#xff0…

【Spring】Spring 总览

一、简单介绍一下 Spring Spring是一个全面的、企业应用开发的一站式解决方案&#xff0c;贯穿表现层、业务层、持久层&#xff0c;可以轻松和其他框架整合&#xff0c;具有轻量级、控制反转、面向切面、容器等特征。 轻量级 &#xff1a; 空间开销和时间开销都很轻量 控制反…

蓝桥杯单片机第十二届国赛 真题+代码

iic.c /* # I2C代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础&#xff0c;根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求&#xff0c;进行代码调试和修改。 */ #include <STC1…

java springBoot 整合日志

1.在Spring Boot项目的resources目录下创建一个新的logback.xml文件。 2.logback.xml中&#xff0c;配置 代码 <?xml version"1.0" encoding"UTF-8"?> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL…

了解Unity编辑器之组件篇Physics 2D(十二)

一、Area Effector 2D区域施加力&#xff09;&#xff1a;用于控制区域施加力的行为 Use Collider Mask&#xff08;使用碰撞器遮罩&#xff09;&#xff1a;启用后&#xff0c;区域施加力仅会作用于特定的碰撞器。可以使用Collider Mask属性选择要作用的碰撞器。 Collider Ma…

vmware中windows操作系统虚拟机安装

1.win10中安装 1.1 虚拟机向导 文件-新建虚拟机 典型-下一步 稍后安装操作系统-下一步 window10 64x -下一步 修改虚拟机名称及位置-下一步 默认60g,至少大于40g-将虚拟磁盘拆分成多个文件夹-下一步 点击完成 1.2 编辑虚拟机设置 移除打印机 设置虚拟机&#xff0c;加入iso映…

【隐式动态求解】使用非线性纽马克方法的隐式动态求解研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

小程序 获取用户头像、昵称、手机号的组件封装(最新版)

在父组件引入该组件 <!-- 授权信息 --><auth-mes showModal"{{showModal}}" idautnMes bind:onConfirm"onConfirm"></auth-mes> 子组件详细代码为: authMes.wxml <!-- components/authMes/authMes.wxml --> <van-popup show…

CompletableFuture生产中使用问题

CompletableFuture生产中使用问题 1 背景2 测试3 原因4. 总结 1 背景 接到一个任务,需要优化下单接口,查看完业务逻辑后发现有一些可以并行或异步查询的地方,于是采用CompletableFuture来做异步优化,提高接口响应速度,伪代码如下 //查询用户信息CompletableFuture<JSONObj…

安全测试国家标准解读——数据库管理和文件管理

下面的系列文章主要围绕《GB/T 38674—2020 信息安全技术 应用软件安全编程指南》进行讲解&#xff0c;该标准是2020年4月28日&#xff0c;由国家市场监督管理总局、国家标准化管理委员会发布&#xff0c;2020年11月01日开始实施。我们对该标准中一些常见的漏洞进行了梳理&…

环境搭建-Ubuntu20.04.6系统TensorFlow BenchMark的GPU测试

1. 下载Ubuntu20.04.6镜像 登录阿里云官方镜像站&#xff1a;阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 2. 测试环境 Server OS&#xff1a;Ubuntu 20.04.6 LTS Kernel: Linux 5.4.0-155-generic x86-64 Docker Version&#xff1a;24.0.5, build ced0996 docker-com…

Django框架:使用channels实现websocket,配置和项目实际使用

一、基本配置 依赖包&#xff1a; Django3.2 django-cors-headers3.5.0 redis4.6.0 #操作redis数据库的 channels3.0.0 #websocket channels-redis4.1.0 #通道层需要&#xff0c;依赖redis包项目目录结构&#xff1a; study_websocket --study_websocket --__init__.py --s…

3dsmax制作一个机器人

文章目录 建模身子&#xff1a;眼睛&#xff1a;头饰&#xff1a;肩膀手臂腿调整细节 渲染导出objMarmoset Toolbag 3.08渲染给眼睛添加材质&#xff0c;设置为自发光添加背景灯光 建模 身子&#xff1a; 眼睛&#xff1a; 头饰&#xff1a; 肩膀 手臂 腿 调整细节 渲染 导出…

Android性能优化之游戏引擎初始化ANR

近期&#xff0c;着手对bugly上的anr 处理&#xff0c;记录下优化的方向。 借用网上的一张图&#xff1a; 这里的anr 问题是属于主线程的call 耗时操作。需要使用trace 来获取发生anr前一些列的耗时方法调用时间&#xff0c;再次梳理业务&#xff0c;才可能解决。 问题1 ja…

【Linux进程】进程控制(下) {进程程序替换:程序替换的工作原理,程序替换函数exec*,简单的命令行解释器}

四、进程程序替换 之前用fork创建子进程后&#xff0c;父子进程执行同一个程序的不同代码段。 如何使子进程执行另一个不同的程序呢&#xff1f;子进程需要进行程序替换&#xff01; 程序替换&#xff0c;就是通过特定的接口&#xff0c;将磁盘上一个全新的程序&#xff08;包…