编织梦想:SpringBoot AOP 教程与自定义日志切面完整实战

什么是 AOP

AOP 是指通过预编译方式和运行期动态代理的方式,在不修改源代码的情况下对程序进行功能增强的一种技术。AOP 不是面向对象编程(OOP)的替代品,而是 OOP 的补充和扩展。它是一个新的维度,用来表达横切问题,并提供一个声明式的处理方案。AOP 是 Spring 框架中的一个重要特性。

AOP 的使用场景

AOP 的使用场景一般是在某些纵向逻辑和多个相对独立的横向逻辑中,将横向逻辑进行抽象和封装,使得横向逻辑不再与纵向逻辑混杂在一起,使得应用程序更加易于维护和扩展。在实际开发中,AOP 的使用场景比较广泛,例如:

  • 日志记录:在应用程序中,可以通过 AOP 对方法调用进行拦截,在方法调用前后记录日志信息。
  • 安全处理:通过 AOP 实现安全方案,例如在应用程序中对某些敏感方法添加权限验证。
  • 性能监控:对应用程序进行性能监控,实现性能分析和调优。
  • 事物管理:通过 AOP 对事物进行管理,例如实现事物的回滚和提交。
  • 缓存管理:对应用程序进行缓存管理,例如在读写操作中进行缓存。

AOP 的核心概念

在学习 AOP 的过程中,有一些核心概念是相当重要的。

  1. 连接点(JointPoint)

连接点是程序中可能被拦截的方法。在 AOP 中,连接点是指所有被拦截到的方法。连接点包含两个信息:一个是方法的位置信息,另一个是方法的名称。

  1. 切点(Pointcut)

切点是一组连接点的集合,是要被拦截的连接点。在 Spring AOP 中,切点采用 AspectJ 的切点表达式进行描述,格式如 @Pointcut("execution(public * com.example.demo.controller.*.*(..))")

  1. 通知(Advice)

通知是指拦截到连接点后要执行的代码,包括 @Before@AfterReturning@AfterThrowing@After@Around 五种类型。

  1. 切面(Aspect)

切面是一个包含通知和切点的对象,主要用来维护切点和通知之间的关系。

  1. 织入(Weaving)

织入是将切面应用到目标对象来创建新的代理对象的过程。在 Spring AOP 中,织入可以在编译时、类加载时和运行时进行。

AOP 的实现方式

在 SpringBoot 中,AOP 的实现方式主要有两种:Java 代理(JDK Proxy)和字节码增强(CGLIB)。

  1. Java 代理(JDK Proxy)

Java 代理是一种基于接口的代理,通过实现 Java 动态代理接口 InvocationHandler 来实现对代理类方法的调用。Java 代理只能代理实现了接口的类,在运行时通过生成代理类的方式来实现。Java 代理的优点是操作简单,劣势是只能代理接口。

  1. 字节码增强(CGLIB)

字节码增强是一种基于继承的代理,通过生成代理类来完成对目标对象方法的调用。CGLIB 代理不需要实现接口,对目标对象进行继承并重写其中的方法,从而实现对方法的调用拦截。字节码增强的优点是能够代理非接口的类,劣势是需要引入 CGLIB 依赖包。

SpringBoot AOP 的例子

我们创建一个日志切面来记录调用方法开始时间、结束时间、持续时间等(方法名、参数、返回值…)。

pom.xml引入以下依赖包

<properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>${spring-boot.version}</version></dependency></dependencies>

定义SysLog日志注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {String name() default "";
}

定义切面 LogAspect

@Aspect
public class LogAspect {// 定义需要被拦截的切点@Pointcut("execution(public * com.example.demo.controller.*.*(..))")public void pointcut() {}// 在方法执行时进行通知@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();SysLog sysLog = method.getAnnotation(SysLog.class);if (sysLog != null) {System.out.println("日志名称:" + sysLog.name());}System.out.println("开始时间:" + LocalDateTime.now());System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 前置环绕通知");Object o = joinPoint.proceed();System.out.println("结束时间:" + LocalDateTime.now());System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 后置环绕通知");return o;}// 在方法执行之前进行通知@Before("pointcut()")public void before(JoinPoint joinPoint) {System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 前置通知");}// 在方法执行之后进行通知@After("pointcut()")public void after(JoinPoint joinPoint) {System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 后置通知");}// 在方法执行之后返回结果时进行通知@AfterReturning(returning = "result", pointcut = "pointcut()")public void afterReturning(JoinPoint joinPoint, Object result) {System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 返回通知,返回值:" + result);}// 在方法抛出异常时进行通知@AfterThrowing(throwing = "ex", pointcut = "pointcut()")public void afterThrowing(JoinPoint joinPoint, Exception ex) {System.err.println("执行方法 " + joinPoint.getSignature().getName() + " 异常通知,异常:" + ex.getMessage());}
}

整个通知流程如下:


注意:如下必须注入LogAspect Bean。

@Configuration
public class MyConfiguration {@Beanpublic LogAspect logAspect() {return new LogAspect();}
}

测试,在Controller方法上添加@SysLog(name = “这是一个日志名称”)注解。

@RestController()
@RequestMapping("user")
public class UserController {@SysLog(name = "这是一个日志名称")@GetMapping("test")public String test() {return "hello world";}
}

测试:访问 http://localhost:8080/user/test,打印如下结果:

日志名称:这是一个日志名称
开始时间:2023-07-17T16:33:11.935
执行方法 test 前置环绕通知
执行方法 test 前置通知
执行方法 test 返回通知,返回值:hello world
执行方法 test 后置通知
结束时间:2023-07-17T16:33:11.940
执行方法 test 后置环绕通知

总结

以上就是 SpringBoot AOP 的基本用法,通过使用 SpringBoot AOP,我们可以在不修改源代码的情况下对程序进行功能增强,实现对方法的拦截、日志记录、权限验证、性能监控等功能。

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

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

相关文章

常见前端基础面试题(HTML,CSS,JS)(三)

JS 中如何进行数据类型的转换&#xff1f; 类型转换可以分为两种&#xff0c;隐性转换和显性转换 显性转换 主要分为三大类&#xff1a;数值类型、字符串类型、布尔类型 三大类的原始类型值的转换规则我就不一一列举了 数值类型&#xff08;引用类型转换&#xff09; Numbe…

设计模式之状态模式(State)的C++实现

1、状态模式的提出 在组件功能开发过程中&#xff0c;某些对象的状态经常面临变化&#xff0c;不同的状态&#xff0c;其对象的操作行为不同。比如根据状态写的if else条件情况&#xff0c;且这种条件变化是经常变化的&#xff0c;这样的代码不易维护。可以使用状态模式解决这…

如何在window下cmd窗口执行linux指令?

1.Git&#xff1a;https://git-scm.com/downloads(官网地址) 2.根据自己的实际路径,添加两个环境变量 3.重启电脑

删除有序链表中重复的元素-II(链表)

乌&#xff01;蒙&#xff01;山&#xff01;连&#xff01;着&#xff01;山&#xff01;外&#xff01;山&#xff01; 题目&#xff1a; 思路&#xff1a; 双指针&#xff0c;slow和fast&#xff0c;并且增加标记flag初始为1。 如果slow指向节点值等于fast指向节点值&…

Servlet 初步学习

文章目录 Servlet1 简介2 快速入门3 执行流程4 生命周期5 方法介绍6 体系结构7 urlPattern配置8 XML配置 Servlet 1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门 动态 web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面…

什么是cURL?

cURL无处不在。它几乎隐藏在所有设备中&#xff0c;例如汽车&#xff0c;蓝光播放器等。它通过互联网协议传输任意类型数据。 在本文中&#xff0c;我们将揭开cURL神秘命令行工具的面纱&#xff0c;解释它是如何成为一种通用代码的&#xff0c;并举例说明其用法。 cURL是什么意…

PHP海外代购管理系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 海外代购管理系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/88229435 论文 https://…

Redis中的分布式锁及其延生的问题

前言 本文将着重介绍Redis中的分布式锁及其与出现的死锁和锁误删问题 什么是分布式锁 首先问题就是什么是分布式锁&#xff0c;分布式锁就是分布式系统中实现并发控制的一种锁机制&#xff0c;它可以保证多个节点在同一个时间只有有一个能成功竞争到系统资源&#xff08;共享…

LeetCode算法递归类—二叉树中的最大路径和

目录 124. 二叉树中的最大路径和 - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a; 代码&#xff1a; 运行结果&#xff1a; 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该…

基于Opencv的虚拟拖拽项目

预备知识 勾股定理 跟随移动算法 手势识别图解 项目源代码 """ 演示一个简单的虚拟拖拽 步骤&#xff1a; 1、opencv 读取视频流 2、在视频图像上画一个方块 3、通过mediapipe库获取手指关节坐标 4、判断手指是否在方块上 5、是&#xff0c;方块跟着移动 6、…

密码学学习笔记(十九):密码学关键术语的解释1

数据加密标准(DES) 数据加密标准是使用最广泛的加密体制&#xff0c;它于1977年被美国国家标准和技术研究所(NIST)采纳为联邦信息处理标准FIPS PUB 46。 DES3DESAES明文分组长度&#xff08;位&#xff09;6464128密文分组长度&#xff08;位&#xff09;6464128密钥长度&…

详解junit

目录 1.概述 2.断言 3.常用注解 3.1.Test 3.2.Before 3.3.After 3.4.BeforeClass 3.5.AfterClass 4.异常测试 5.超时测试 6.参数化测试 1.概述 什么是单元测试&#xff1a; 单元测试&#xff0c;是针对最小的功能单元编写测试代码&#xff0c;在JAVA中最小的功能单…

0101读写分离测试-jdbc-shardingsphere-中间件

文章目录 1 前言2、创建SpringBoot程序2.1、创建项目2.2、添加依赖2.3、生成实体类、service与Mapper1.5、配置读写分离 2、测试2.1、读写分离测试2.2、事务测试2.3、负载均衡测试 结语 1 前言 shardingshpere-jdbc定位为轻量级 Java 框架&#xff0c;在 Java 的 JDBC 层提供的…

【前端面试】中大文件上传/下载:中等文件代理服务器放行+大文件切片传输+并发请求+localstorage实现断点续传

目录 中等文件代理服务器放行&#xff1a;10MB为单位 proxy nginx 大文件切片&#xff1a;100MB为单位 断点&#xff1a;存储切片hash 前端方案A localstorage 后端方案B 服务端 上传 前端 后端 下载 前端 后端 多个大文件传输&#xff1a;spark-md5 哈希碰撞…

什么是Pytorch?

当谈及深度学习框架时&#xff0c;PyTorch 是当今备受欢迎的选择之一。作为一个开源的机器学习库&#xff0c;PyTorch 为研究人员和开发者们提供了一个强大的工具来构建、训练以及部署各种深度学习模型。你可能会问&#xff0c;PyTorch 是什么&#xff0c;它有什么特点&#xf…

JQuery快速入门教程

1、JQuery快速入门 1.1、JQuery介绍 jQuery 是一个 JavaScript 库。所谓的库&#xff0c;就是一个 JS 文件&#xff0c;里面封装了很多预定义的函数&#xff0c;比如获取元素&#xff0c;执行隐藏、移动等&#xff0c;目的就 是在使用时直接调用&#xff0c;不需要再重复定义…

微机原理与接口技术 学习笔记(二) 存储器

文章目录 一&#xff0c;存储器1.1 概述1.1.1 半导体存储器的分类按制造工艺&#xff1a; 易失性或挥发性存储器 / 不易失性或不挥发性存储器按制造工艺&#xff1a; 1.1.2 半导体存储器的性能指标1.1.3 半导体存储器的一般结构及组成 1.2 随机存取存储器 RAM1.2.1 静态RAM1.2.…

UML图绘制 -- 类图

1.类图的画法 类 整体是个矩形&#xff0c;第一层类名&#xff0c;第二层属性&#xff0c;第三层方法。 &#xff1a;public- : private# : protected空格: 默认的default 对应的类写法。 public class Student {public String name;public Integer age;protected I…

机器学习知识点总结:什么是GBDT(梯度提升树)

什么是GBDT(梯度提升树) 虽然GBDT同样由许多决策树组成&#xff0c;但它与随机森林由许多不同。 其中之一是GBDT中的树都是回归树&#xff0c;树有分类有回归&#xff0c;区分它们的方法很简单。将苹果单纯分为好与坏的是分类树&#xff0c;如果能为苹果的好坏程度打个分&…

pycharm上传项目到github,版本管理

前提&#xff1a;下载git 设置Git路径 登录Github 此时自动打开浏览器&#xff0c;并打开连接页面&#xff0c;点击 Authorize GitHub。登录&#xff1a; 创建本地仓库 提交到Github 填写初始提交相关信息 origin&#xff0c;它们只是远程服务器的一个别名&#xff0c;否则你就…