【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,一经查实,立即删除!

相关文章

生成图形验证码

4.3.1.1 导入工具类 (1) 导入Constants 常量类 /*** 通用常量类* author spikeCong* date 2023/5/3**/ public class Constants {/*** UTF-8 字符集*/public static final String UTF8 "UTF-8";/*** GBK 字符集*/public static final String GBK "GBK"…

前端魔法进阶:Vue 3源码解析与新特性对比!

一、引言 Vue 3作为前端开发的魔法杖&#xff0c;为我们带来了更快、更小、更强大的全新体验。它的源码是前端领域的宝藏&#xff0c;隐藏着无数神秘的魔法。在本篇博客中&#xff0c;我将带你踏上一段探索Vue 3源码之旅&#xff0c;解析这个前端魔法的奥秘&#xff0c;让你深…

负载均衡的策略有哪些? 负载均衡的三种方式?

负载均衡的策略有哪些? 负载均衡的策略有如下&#xff1a; 1. 轮询&#xff08;Round Robin&#xff09;&#xff1a;按照请求的顺序轮流分配到不同的服务器。 2. 权重&#xff08;Weighted&#xff09;&#xff1a;给不同的服务器分配不同的权重&#xff0c;根据权重比例来…

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

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

【vim 学习系列文章 2 - vim 常用插件配置】

文章目录 1.1 vim 常用插件1.1.1 vim 插件 Pathogen 管理1.1.2 vim 常用插件推荐1.1.3 vim Leaderf1.1.4 vim ripgrep 工具1.1.5 vim Leaderf 配合 rg1.1.6 vim autocmd 配置 1.2 其它类型文件 vimrc 配置1.2.1 System Verilog vimrc 配置 上篇文章&#xff1a;vim 学习系列文章…

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…

VBA操作WORD(八)设置标题格式(含主、副标题)

因为主标题和副标题一般都是包含一两句子的段落&#xff0c;所以参数直接传入Paragraph。至于判断主副标题的规则则外面调用部分再做判断。 Sub 设置主标题格式(ib As Paragraph)With ActiveDocument.Paragraphs(1).Range.Style ActiveDocument.Styles(wdStyleHeading1)设置为…

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

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

uView 在 uni-app 中的使用

文章目录 一、uView是什么&#xff1f;1.uView 安装2.uView 在 uni-app 中的使用 一、uView是什么&#xff1f; 提示&#xff1a;正文内容&#xff1a; uView 官网&#xff1a; https://www.uviewui.com uView 是 uni-app 生态专用的 UI 框架 关于uView的取名来由&#xff0c…

vue中预览静态pdf文件

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

学生管理系统-03项目案例(3)

一、用户列表 1、编写api接口 //导入封装后的axios import {instance} from /util/request export default{getUsers:params>instance.get(/users/getUsers,{params}) } 2、表格渲染 <template><el-card><!-- 当el-table元素中注入data对象数组后&#x…

React之组件间通信

React之组件间通信 组件通信&#xff1a; 简单讲就是组件之间的传值&#xff0c;包括state、函数等 1、父子组件通信 父组件给子组件传值 核心&#xff1a;1、自定义属性&#xff1b;2、props 父组件中: 自定义属性传值 import Header from /components/Headerconst Home ()…

关于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…

antd vue tree的增删改和拖拽

最近项目中遇到一个tree型数据的的操作的功能&#xff0c;代码简单如下&#xff1a; <a-treeshowLineshowIcon:draggable"draggable":expandedKeys"expandedKeys":treeData"treeData"drop"onDrag"expand"onExpand">&l…

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映…