4.12 SpringBoot整合AOP ❤❤❤

1. AOP简介

要介绍面向切面编程(Aspect-Oriented Programming,AOP),需要读者
首先考虑这样一个场景:
公司有一个人力资源管理系统目前已经上线,但是系统运行不稳定,有时运行得很慢,为了检测出到底是哪个环节出问题了,开发人员想要监控每一个方法的执行时间,再根据这些执行时间判断出问题所在。当问题解决后,再把这些监控移除掉
系统目前已经运行,如果手动修改系统中成千上万个方法,那么工作量未免太大,而且这些监控方法以后还要移除掉;如果能够在系统运行过程中动态添加代码,就能很好地解决这个需求
这种在系统运行时动态添加代码的方式称为面向切面编程(AOP)
Spring框架对AOP提供了很好的支持。在AOP中,有一些常见的概念需要读者了解。

  • Joinpoint(连接点): 类里面可以被增强的方法即为连接点。例如,想修改哪个方法的功能,那么该方法就是一个连接点。
  • Pointcut(切入点): 对Joinpoint进行拦截的定义即为切入点。例如,拦截所有以insert开始的方法,这个定义即为切入点。
  • Advice(通知): 拦截到Joinpoint之后所要做的事情就是通知。例如,上文说到的打印日志监控。通知分为前置通知、后置通知、异常通知、最终通知和环绕通知。
  • Aspect(切面):Pointcut和Advice的结合。
  • Target(目标对象):要增强的类称为Target。

2. SpringBoot支持

SpringBoot在Spring的基础上对AOP的配置提供了自动化配置解决方案spring-boot-starter-aop,使开发者能够更加便捷地在SpringBoot项目中使用AOP。
配置步骤如下。

2.1 AOP依赖

首先在SpringBootWeb项目中引入spring-boot-starter-aop依赖,代码如下:

        <!-- SpringBoot 拦截器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

2.2 定义切面

接下来创建切面,代码如下:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Aspect
public class LogAspect1 {/*** 切入点为:com.ruoyi.system.service包下的所有类的所有方法*/@Pointcut("execution(* com.ruoyi.system.service.*.* (..))")public void pc1() {}/*** 前置通知,对切入点pc1()进行增强** @param jp 连接点:包含增强的方法信息*/@Before(value = "pc1()")public void before(JoinPoint jp) {String name = jp.getSignature().getName();System.out.println("Before 前置通知 >>" + name + "方法开始执行...");}/*** 后置通知,对切入点pc1()进行增强** @param jp 连接点:包含增强的方法信息*/@After(value = "pc1()")public void after(JoinPoint jp) {String name = jp.getSignature().getName();System.out.println("After 后置通知 >>" + name + "方法结束执行...");}/*** 返回通知,对切入点pc1()进行增强** @param jp     连接点:包含增强的方法信息* @param result 方法返回值*/@AfterReturning(value = "pc1()", returning = "result")public void afterReturning(JoinPoint jp, Object result) {String name = jp.getSignature().getName();System.out.println("AfterReturning 返回通知 >>" + name + "方法返回值:" + result);}/*** 异常通知,对切入点pc1()进行增强** @param jp 连接点:包含增强的方法信息* @param e  抛出的异常信息*/@AfterThrowing(value = "pc1()", throwing = "e")public void afterReturning(JoinPoint jp, Exception e) {String name = jp.getSignature().getName();System.out.println("AfterThrowing 异常通知 >>" + name + "方法抛出异常,异常信息" + e.getMessage());}/*** 环绕通知** @param pjp* @return* @throws Throwable*/@Around("pc1()")public Object around(ProceedingJoinPoint pjp) {String name = pjp.getSignature().getName();System.out.println("Around 环绕通知 >>" + name + "方法开始执行...");try {return pjp.proceed();} catch (Throwable e) {System.out.println("Around 环绕通知 >>" + name + "方法发生异常...");throw new RuntimeException(e);} finally {System.out.println("Around 环绕通知 >>" + name + "方法结束执行...");}}}

代码解释:

  • @Aspect注解表明这是一个切面类。
  • 第12~14行定义的pc1方法使用了@Pointcut注解,这是一个切入点定义。execution中的第一个*表示方法返回任意值,第二个*表示service包下的任意类,第三个*表示类中的任意方法,括号中的两个点表示方法参数任意,即这里描述的切入点为service包下所有类中的所有方法。
  • 第21~25行定义的方法使用了@Before注解,表示这是一个前置通知,该方法在目标方法执行之前执行。通过JoinPoint参数可以获取目标方法的方法名、修饰符等信息。
  • 第32~36行定义的方法使用了@After注解,表示这是一个后置通知,该方法在目标方法执行之后执行。
  • 第44~48行定义的方法使用了@AfterReturning注解,表示这是一个返回通知,在该方法中可以获取目标方法的返回值。@AfterReturning注解的returning参数是指返回值的变量名,对应方法的参数。注意,在方法参数中定义了result的类型为Object,表示目标方法的返回值可以是任意类型,若result参数的类型为Long,则该方法只能处理目标方法返回值为Long的情况。
  • 第56~60行定义的方法使用了@AfterThrowing注解,表示这是一个异常通知,即当目标方法发生异常时,该方法会被调用,异常类型为Exception表示所有的异常都会进入该方法中执行,若异常类型为ArithmeticException,则表示只有目标方法抛出的ArithmeticException异常才会进入该方法中处理。
  • 第69~81行定义的方法使用了@Around注解,表示这是一个环绕通知。环绕通知是所有通知里功能最为强大的通知,可以实现前置通知、后置通知、异常通知以及返回通知的功能。目标方法进入环绕通知后,通过调用ProceedingJoinPoint对象的proceed方法使目标方法继续执行,开发者可以在此修改目标方法的执行参数、返回值等,并且可以在此处理目标方法的异常。

配置完成
测试

17:17:35.368 [http-nio-8080-exec-1] INFO  o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
Around 环绕通知 >>selectUserList方法开始执行...
Before 前置通知 >>selectUserList方法开始执行...
17:17:35.623 [http-nio-8080-exec-1] DEBUG c.r.s.m.S.selectUserList_COUNT - [debug,135] - ==>  Preparing: SELECT count(0) FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.del_flag = '0'
17:17:35.628 [http-nio-8080-exec-1] DEBUG c.r.s.m.S.selectUserList_COUNT - [debug,135] - ==> Parameters: 
17:17:35.630 [http-nio-8080-exec-1] DEBUG c.r.s.m.S.selectUserList_COUNT - [debug,135] - <==      Total: 1
17:17:35.633 [http-nio-8080-exec-1] DEBUG c.r.s.m.S.selectUserList - [debug,135] - ==>  Preparing: select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u left join sys_dept d on u.dept_id = d.dept_id where u.del_flag = '0' LIMIT ?
17:17:35.634 [http-nio-8080-exec-1] DEBUG c.r.s.m.S.selectUserList - [debug,135] - ==> Parameters: 10(Integer)
17:17:35.637 [http-nio-8080-exec-1] DEBUG c.r.s.m.S.selectUserList - [debug,135] - <==      Total: 2
AfterReturning 返回通知 >>selectUserList方法返回值:Page{count=true, pageNum=1, pageSize=10, startRow=0, endRow=10, total=2, pages=1, reasonable=true, pageSizeZero=false}
After 后置通知 >>selectUserList方法结束执行...
Around 环绕通知 >>selectUserList方法结束执行...

异常测试

Around 环绕通知 >>selectErr方法开始执行...
Before 前置通知 >>selectErr方法开始执行...
AfterThrowing 异常通知 >>selectErr方法抛出异常,异常信息null
After 后置通知 >>selectErr方法结束执行...
Around 环绕通知 >>selectErr方法发生异常...
Around 环绕通知 >>selectErr方法结束执行...
17:23:08.159 [http-nio-8080-exec-3] ERROR c.r.f.w.e.GlobalExceptionHandler - [handleRuntimeException,93] - 请求地址'/system/user/err',发生未知异常.
java.lang.RuntimeException: com.ruoyi.common.exception.ServiceException

**********************************************************

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

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

相关文章

错了,全部错了

感觉还是挺有难度的吧。。 其实最小割在这种最大/最小化代价的问题上的拓展性是比最大流要好的&#xff0c;甚至有些问题用最大流建模后不好求而要转化为求最小割&#xff08;例如 CF1368H Breadboard Capacity&#xff0c;但是也不绝对&#xff0c;有极少数题是用最小割建模…

力扣HOT100 - 2. 两数相加

解题思路&#xff1a; 缺位的节点进行补零处理&#xff0c;如97323补充为973023 注意相加的进位问题 class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode head null, tail null;int carry 0;while (l1 ! null || l2 ! null) {int n1 l…

2013-2021年各省经济韧性相关测度指标面板数据

2013-2021年各省经济韧性相关测度指标面板数据 1、时间&#xff1a;2013-2021年 2、指标&#xff1a;城镇化率 %、财政科学技术支出&#xff08;亿元&#xff09;、万人高等教育在校人数&#xff08;万人&#xff09;、财政教育支出&#xff08;亿元&#xff09;、第三产业占…

semaphore信号量使用+原理分析

1.概述 Semaphore 信号量&#xff0c;相当于一个计数器&#xff0c;通常用来限制线程的数量。 每个线程操作前会先获取一个许可证&#xff0c;逻辑处理完成之后就归还这个许可证。 通俗的解释&#xff1a;相当于一个停车场&#xff0c;有10个停车位&#xff0c;进来一个车&am…

QtCharts 组件

Qtcharts 组件基于GraphicsView模式实现&#xff0c;其核心是QChartView和QChart的二次封装版。 在pro文件中包含QT charts来引入绘图类库。 头文件中定义QT_CHARTS_USE_NAMESPACE宏&#xff0c;这样才可以正常的使用绘图功能。 一般情况下我们会在mainwindows.h头文件中增…

本地部署Docker容器可视化图形管理工具DockerUI并实现无公网IP远程访问——“cpolar内网穿透”

文章目录 前言1. 安装部署DockerUI2. 安装cpolar内网穿透3. 配置DockerUI公网访问地址4. 公网远程访问DockerUI5. 固定DockerUI公网地址 前言 DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基…

stack、queue(priority_queue)的模拟实现和deque的简单介绍

stack和queue(priority_queue) 1. 容器适配器 适配器(Adapter)&#xff1a;一种用来修饰容器(Containers)或仿函数(Functors)或迭代器(Iterator)接口的东西。 适配器是一种设计模式&#xff0c;该模式将一个类的接口转换成客户希望的另外一个接口。 现实中拿插座来说&#xf…

LeetCode 每日一题 Day 137-143

928. 尽量减少恶意软件的传播 II(Hard) 给定一个由 n 个节点组成的网络&#xff0c;用 n x n 个邻接矩阵 graph 表示。在节点网络中&#xff0c;只有当 graph[i][j] 1 时&#xff0c;节点 i 能够直接连接到另一个节点 j。 一些节点 initial 最初被恶意软件感染。只要两个节点…

llm构建知识库

构建词向量库 在RAG应用中&#xff0c;我们需要大模型调用知识库中的专业内容对用户的提问进行更加完善的回答&#xff0c;因此如何为llm引入知识库便尤为关键&#xff0c;在本篇内容中&#xff0c;我会从基础的词向量开始介绍&#xff0c;最终到如何完成一个可供llm调用的向量…

C语言进阶课程学习记录- 函数与宏分析

C语言进阶课程学习记录- 函数与宏分析 实验-宏和函数实验-宏的副作用实验-宏的妙用小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 实验-宏和函数 #include <stdio.h>#define RESET(p, len) …

程序员开发必备,开发资源资料分享【3】

第2部分内容 090-100036401-专栏课-聂鹏程-分布式技术原理与算法解析&#xff08;完结&#xff09;提取码&#xff1a; 091-100036501-专栏课-王健-说透中台&#xff08;完结&#xff09;提取码&#xff1a; 092-100036601-专栏课-石雪峰-DevOps 实战笔记&#xff08;完结&a…

【Nginx】centos和Ubuntu操作系统下载Nginx配置文件并启动Nginx服务详解

目录 &#x1f337; 安装Nginx环境 &#x1f340; centos操作系统 &#x1f340; ubuntu操作系统 &#x1f337; 安装Nginx环境 以下是在linux系统中安装Nginx的步骤&#xff1a; 查看服务器属于哪个操作系统 cat /etc/os-release安装 yum&#xff1a; 如果你确定你的系统…

Spring是如何解决循环依赖的

简介 所谓循环依赖指的是&#xff1a;BeanA对象的创建依赖于BeanB&#xff0c;BeanB对象的创建也依赖于BeanA&#xff0c;这就造成了死循环&#xff0c;如果不做处理的话势必会造成栈溢出。Spring通过提前曝光机制&#xff0c;利用三级缓存解决循环依赖问题。 变量描述single…

RU 19.23 安装

分别通过对GRID_HOME和ORACLE_HOME进行安装补丁的方式。来安装RU19.23 。 前面的相关检查等步骤&#xff0c;略。 安装RU的过程 1 分别最GRID_HOME安装RU 2 分别对ORACLE_HOME安装RU 3 升级catalog &#xff08;如有必要&#xff09; 4 其他事项 1 对GRID HOME安装ru # opatch…

【C语言__指针01__复习篇11】

目录 前言 一、什么是指针 二、计算机中常见的单位 三、CPU是怎样找到一块内存空间的 四、如何得到变量的地址 五、指针变量 六、解引用指针变量的作用 七、指针变量的大小 八、指针变量类型的意义 8.1 指针的解引用 8.2 指针-整数 九、void*指针 十、const修饰变…

搜维尔科技:Haption Virtuose 6D TAO 力反馈设备,专为机器人的和遥操作市场设计

HaptionVirtuose 6D TAO 力反馈设备&#xff0c;专为机器人的和遥操作市场设计 搜维尔科技&#xff1a;Haption Virtuose 6D TAO 力反馈设备&#xff0c;专为机器人的和遥操作市场设计

【Ansible】01

自动化运维 Ansible Ansible首发于2012年 , 2015年被收购 Ansible是一款自动化运维工具 , 基于 Python 开发 基于ssh进行管理 , 不需要在被管理端安装任何软件 Ansible主要是通过各种模块进行操作 批量系统配置 批量程序部署 批量运行命令等功能 环境准备 控制节点主…

PCB走线宽度、PCB走线宽度计算、PCB走线宽度和电流

目录 一、什么是PCB走线宽度&#xff1f; 二、什么是走线&#xff1f; 三、哪些因素对走线宽度至关重要&#xff1f; 1、信号走线 2、电源走线 3、直线宽度和信号反射 四、怎么计算PCB走线宽度&#xff1f; 1、使用PCB走线宽度计算器 2、使用方程式 五、怎么计算PCB 走…

UGUI父对象自适应子元素布局解决方案

问题描述 在UI开发中&#xff0c;难免会遇到需要父对象自适应子元素尺寸的问题&#xff0c;这通常是为了解决不同屏幕分辨率的UI自适应问题。实际上我们去仔细了解UGUI的布局组件的原理就能够合理配置组件来解决这个问题。 案例问题&#xff1a;例如我现在有一个背景面板&#…

25计算机考研院校数据分析 | 上海交通大学

上海交通大学电子信息与电气工程学院成立于2001年12月&#xff0c;其前身可湖源至百年前的电机专科&#xff0c;具有中国电气工程师“摇篮”之美称。50年代根据学科发展需要分为电工与计算机科学系(三系)和电子工程系(四系)。1985年&#xff0c;三系和四系合并&#xff0c;成立…