当前位置: 首页 > news >正文

自定义 Spring AOP 切面实战(鉴权、记录日志)

前言:

从事 Java 的小伙伴都知道 Spring AOP,也都知道 AOP 是面向切面编程,那你知道 AOP 在项目实战中怎么使用吗?本篇简单分享 Spring AOP 在项目中的实际使用。

AOP 知识储备传送门:

深入理解 Spring AOP 源码分析(附源码分析)

AOP 基础知识回顾

AOP 概念

  • 连接点(Join point):能够被拦截的地⽅、地点,Spring AOP 是基于动态代理的,所以是⽅法拦截的,每个成员⽅法都可以称之为连接点。
  • 切点(Poincut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联,切点分为execution方式和annotation方式,前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
  • 通知(Advice):在切面的某个特定的连接点上执行的动作,通知分为:前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能)。
  • 织⼊(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入
  • 引⼊/引介(Introduction):允许我们向现有的类添加新⽅法或属性,是⼀种特殊的增强。
  • 切面(Aspect):切⾯由切点和增强、通知组成,它既包括了横切逻辑的定义、也包括了连接点的定义。

通知(Advice)分类

  • 前置通知(Before Advice):在⽬标⽅法被调⽤前的通知功能。
  • 后置通知(After Advice):在⽬标⽅法被调⽤之后的通知功能。
  • 返回通知(After-returning):在⽬标⽅法成功执⾏之后的通知功能。
  • 异常通知(After-throwing):在⽬标⽅法抛出异常之后的通知功能。
  • 环绕通知(Around):把整个⽬标⽅法包裹起来,在被调⽤前和调⽤之后的通知功能。

自定义 AOP 切面常用知识储备

@Aspect 注解的作用?

@Aspect 注解的作用是定义一个切面,需要再自定义切面类上加上次注解。

@Component 注解

@Component 注解表示这个类交给 Spring 容器管理,如果不使用 @Component 注解,也要以其他方法是注册切面类,以确保 Spring 容器能够识别并管理这个切面。

Pointcut 切点中 execution 和 @annotation 定义切面的区别?

  • @annotation:切点表达式是注解的全限类名,是根据注解来匹配的,有注解的方法才会被拦截。

  • execution:切点表达式很灵活,有修饰符匹配、返回值匹配、类路径匹配、方法名匹配、参数匹配、异常类型匹配。

  • 作用于任意以 public 修饰的方法:execution(public * *(…))

  • 作用于任何一个以 set 开始的方法:execution(* set*(…))

  • 作用于 MyService 接口的任意方法:execution(* com.xxx.service.MyService.*

  • 作用于定义在 service 包和所有子包里的任意类的任意方法:execution(* com.xxx.service….(…)),第一个 * 表示匹配任意的方法返回值, …(两个点)表示零个或多个,第一个 … 表示 service 包及其子包,第二个 * 表示所有类,第三个 * 表示所有方法,第二个…表示方法的任意参数个数。

JoinPoint 的作用?

PoinPoint 代表程序中的一个点,通常把 JoinPoint 作为一个参数传递到通知方法中,通过 JoinPoint 来获取程序中这个点的相关信息,JoinPoint 只能用于 @Before、@After、@AfterReturning、@AfterThrowing 通知中,不能使用于 @Around 中,JoinPoint 常用方法如下:

  • Object[] getArgs():返回目标方法的参数。
  • Signature getSignature():返回方法的签名。
  • Object getTarget():返回被增强的目标对象,也就是被代理对象。
  • Object getThis():返回由目标对象生成的代理对象。

ProceedingJoinPoint 的作用?

ProceedingJoinPoint 是 JoinPoint 的子接口,用于环绕通知 @Around 中,它提供了一个 proceed() 方法,用于执行被拦截的方法也就是目标方法。

自定义 AOP 切面代码演示

自定义 AOP 切面实现接口请求日志记录

记录接口请求日志这种业务场景是非常常见的,如果在每个接口中去记录,显得代码臃肿且不灵活,而且有很大的侵入性,我们可以使用自定义 AOP 面来完成这种功能,代码如下:

@Slf4j
@Component
@Aspect
public class MyApiLogAspect implements Ordered {@Pointcut("execution(* com.my.study.controller..*(..))")public void logPointCut() {}@Around("logPointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();//获取当前请求对象ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();//记录请求日志信息ApiLogVO apiLogDO = new ApiLogVO();MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();String methodName = method.getName();apiLogDO.setMethodName(methodName);apiLogDO.setRequestMethod(request.getMethod());apiLogDO.setMethodParameters(getParameter(method, joinPoint.getArgs()));apiLogDO.setUri(request.getRequestURI());//执行目标方法Object result = joinPoint.proceed();apiLogDO.setResponse(result);long endTime = System.currentTimeMillis();apiLogDO.setTimeConsuming((endTime - startTime));log.info("请求日志信息:{}", JSON.toJSONString(apiLogDO));return result;}/*** 根据方法和传入的参数获取请求参数*/private Object getParameter(Method method, Object[] args) {List<Object> argList = new ArrayList<>();Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {//将RequestBody注解修饰的参数作为请求参数RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);if (requestBody != null) {argList.add(args[i]);}//将RequestParam注解修饰的参数作为请求参数RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);if (requestParam != null) {Map<String, Object> map = new HashMap<>();String key = parameters[i].getName();if (ObjectUtil.isNotEmpty(requestParam.value())) {key = requestParam.value();}map.put(key, args[i]);argList.add(map);}}if (argList.size() == 0) {return null;} else if (argList.size() == 1) {return argList.get(0);} else {return argList;}}@Overridepublic int getOrder() {return -1;}
}@Data
public class ApiLogVO {@ApiModelProperty("方法名称")private String methodName;@ApiModelProperty("请求方式")private String requestMethod;@ApiModelProperty("方法路径")private String uri;@ApiModelProperty("方法耗时")private Long timeConsuming;@ApiModelProperty("方法参数")private Object methodParameters;@ApiModelProperty("方法响应")private Object response;}

从代码来看,我们使用了很少的代码量完成了接口请求日志的记录,而且没有入侵业务代码,Pointcut 支持各种灵活配置,可以丰富我们记录各种场景,因为记录了接口出入参及请求时间,因此我们使用的是环绕通知。

自定义 AOP 切面实现接口鉴权

在业务项目开发中,部分接口需要进行权限校验,部分接口不需要进行权限校验,这也是一种非常常见的场景,我们如果在每个接口中去做判断同样显得代码臃肿且入侵业务代码,我们同样可以通过自定义注解 + AOP 切面的方式完成接口鉴权的功能,代码如下:

@Component
@Slf4j
@Aspect
public class MyAuthLogAspect implements Ordered {@Resourceprivate AuthUtil authUtil;@Pointcut("@annotation(com.com.my.study.annotation.RequiresPermissions)")public void authPointCut() {}@Before("authPointCut()")public void before(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 注解鉴权 有注解表示需要鉴权RequiresPermissions requiresPermissions = signature.getMethod().getAnnotation(RequiresPermissions.class);if (requiresPermissions != null) {authUtil.checkPermission(requiresPermissions);}}@After("authPointCut()")public void after(JoinPoint joinPoint) {}@Overridepublic int getOrder() {//优先级最高  值越小 越先执行return 1;}
}

通过代码可以看出通过自定义 AOP 切面完成接口权限校验也是十分简单方便,因为是进行接口权限校验,我们使用前置通知即可完成接口权限校验的功能。

自定义 AOP 切面执行顺序

当项目中有多个切面的时候,我们该如何保证切面的执行顺序?我们可以通过实现 Order 接口来自定义切面的执行顺序,order 的值越小,对应的切面的优先级就越高,也就越先执行。

自定义 AOP 切面的使用场景

  • 接口日志记录。
  • 权限校验。
  • 接口限流。
  • 。。。。。。

欢迎提出建议及对错误的地方指出纠正。

相关文章:

自定义 Spring AOP 切面实战(鉴权、记录日志)

前言&#xff1a; 从事 Java 的小伙伴都知道 Spring AOP&#xff0c;也都知道 AOP 是面向切面编程&#xff0c;那你知道 AOP 在项目实战中怎么使用吗&#xff1f;本篇简单分享 Spring AOP 在项目中的实际使用。 AOP 知识储备传送门&#xff1a; 深入理解 Spring AOP 源码分析…...

JAVA面试题大全(九)

1、为什么要使用 spring&#xff1f; 方便解耦&#xff0c;便于开发支持aop编程声明式事务的支持方便程序的测试方便集成各种优秀的框架降低JavaEE API的使用难度 2、解释一下什么是 aop&#xff1f; AOP 是 Aspect-Oriented Programming 的缩写&#xff0c;中文翻译为“面向…...

React 组件三大核心之 ref

文章目录 用法React.createRef()useRef Hook 注意 ref 是 React 中的一个重要概念&#xff0c;它用于访问和操作 DOM 元素或者类组件实例。 在React中&#xff0c;ref 提供了一种方式&#xff0c;允许我们访问DOM节点或在render方法中创建的React元素。这对于执行DOM操作、读取…...

json 读写 python

目录 这里对json保存做了格式封装 调用代码&#xff1a; python json原始保存是所有json保存一行&#xff0c; 这里对json保存做了格式封装 import numpy as np class MyEncoder(json.JSONEncoder):def default(self, obj):if isinstance(obj, np.integer):return int(obj)…...

Docker快速部署Seata的TC服务以及微服务引入Seata教程

目录 一、使用docker部署Seata的TC服务 1、拉取TC服务镜像 2、创建并运行容器 ​3、修改配置文件 4、在Nacos中添加TC服务的配置 5、重启TC服务 二、微服务集成Seata 1、引入依赖 2、修改配置文件 Seata是阿里的一个开源的分布式事务解决方案&#xff0c;能够为分布…...

我的第一个JAVA程序IDEA版

目录 第一步 新建一个空项目第二步 新建模块第三步 新建包第四步 新建类第五步 新建main方法 第一步 新建一个空项目 第二步 新建模块 第三步 新建包 第四步 新建类 然后在包文件夹下新建类 第五步 新建main方法...

轻量SEO分析报告程序网站已开心去授权

轻量SEO分析报告程序网站已开心去授权&#xff0c;可以让你生成有洞察力的、 简洁的、易于理解的SEO报告&#xff0c;帮助你的网页排名和表现更好 网站源码免费下载地址抄笔记 (chaobiji.cn)https://chaobiji.cn/...

本机与华为云ping不通的问题

进入华为云控制台。依次选择&#xff1a;云服务器->点击服务器id->安全组->更改安全组->添加入方向规则&#xff0c;添加一个安全组规则&#xff08;ICMP&#xff09;&#xff0c;详见下图 再次ping公网ip就可以ping通了 产生这一问题的原因是ping的协议基于ICMP…...

FastJSON2 > FastJSON 好在何处

FastJSON 是一种广泛使用的 JSON 解析库&#xff0c;其高性能和简单易用的特点受到开发者的喜爱。然而&#xff0c;随着应用场景的复杂化和安全要求的提高&#xff0c;FastJSON 逐渐暴露出一些问题。为了解决这些问题并进一步提升性能和安全性&#xff0c;阿里巴巴推出了 FastJ…...

7个常见的SQL慢查询问题及其解决方法

大家好&#xff0c;得益于摩尔定律&#xff0c;计算机性能已大幅提升&#xff0c;加上数据库的进步以及微服务所倡导的各种反模式设计&#xff0c;因此现在编写复杂SQL查询的机会越来越少。业界已经开始提倡不要进行专门的SQL优化&#xff0c;因为节省下来的资源并不足以抵消员…...

《Rust奇幻之旅:从Java和C++开启》第1章Hello world 1/5

讲动人的故事,写懂人的代码 很多程序员都在自学Rust。 🤕但Rust的学习曲线是真的陡,让人有点儿怵头。 程序员工作压力大,能用来自学新东西的时间简直就是凤毛麟角。 📕目前,在豆瓣上有7本Rust入门同类书。它们虽有高分评价,但仍存在不足。 首先,就是它们介绍的Rust新…...

将富文本编辑器中的H标签处理成树形结构,支持无限层级

做富文本编辑器时&#xff0c;需要将文本里的标题整理成树形数据&#xff0c; // 这里是数据结构 const data [{"id": "hkyrq2ndc-36yttda0lme00","text": "阿萨德阿萨德阿萨","level": 1,"depth": 1,},{"…...

探索移动云:我的ES与Kibana之旅

目录 引言&#xff1a; 如何免费体验移动云产品 登录并完成实名认证 选择试用ECS云主机 安全组配置 安装Elasticsearch和Kibana 安装Elasticsearch ​编辑安装kibana 测试结果 使用感觉 引言&#xff1a; 移动云技术产品的发展已经取得了巨大的进步。云数融合、A1、大…...

java 线程执行原理,java线程在jvm中执行流程

java 线程执行原理&#xff0c;java线程在jvm中执行流程 从jvm视角看java线程执行过程 ##首先thread.c注册jni函数 JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) {(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(…...

[Redis]基本全局命令

Redis存储方式介绍 在 Redis 中数据是以键值对的凡事存储的&#xff0c;键&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;是基本的数据存储单元。以下是对 Redis 键值对的详细讲解&#xff1a; 键&#xff08;Key&#xff09;&#xff1a; 类型&#xff1a;…...

【Linux】- HBase集群部署 [19]

简介 apache HBase是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库。 和Redis一样&#xff0c;HBase是一款KeyValue型存储的数据库。 不过和Redis涉及方向不同 Redis设计为少量数据&#xff0c;超快检索HBase设计为海量数据&#xff0c;快速检索 HBase在大数据邻域…...

js如何遍历FormData的值

遍历FormData的值&#xff0c;一般有2种方法&#xff1a;forEach 和 for...of entries const data new FormData();data.append(aaa, 111); data.append(bbb, 222);// 方法1 data.forEach((value, key) > {console.log(key, value); }) 输出 aaa 111 和 bbb 222// 方法2 …...

【C语言】明析部分C语言内存函数

目录 1.memcpy 2.memmove 3.memset 4.memcmp 以下都是内存函数&#xff0c;作用单位均是字节 1.memcpy memcpy是C/C语言中的一个内存拷贝函数&#xff0c;其原型为&#xff1a; void* memcpy(void* dest, const void* src, size_t n);目标空间&#xff08;字节&#xff09…...

一阶数字高通滤波器

本文的主要内容包含一阶高通滤波器公式的推导和数字算法的实现以及编程和仿真 1 计算公式推导 1.1.2 算法实现及仿真 利用python实现的代码如下&#xff1a; import numpy as np # from scipy.signal import butter, lfilter, freqz import matplotlib.pyplot as plt #2pifW…...

Linux多线程系列2: 模拟封装简易语言级线程库,线程互斥和锁,线程同步和条件变量,线程其他知识点

Linux多线程系列2: 模拟封装简易语言级线程库,线程互斥和互斥锁,线程同步和条件变量,线程其他知识点 1.前言 一.模拟C11线程库自己封装简易语言级线程库1.实现框架2.迅速把构造等等函数写完3.start和work1.尝试一2.尝试二3.最终版本4.给出代码 二.模拟实现多线程(为编写线程池做…...

lychee-rerank-mm行业方案:文旅部门景区图片库按游客搜索词智能排序

Lychee-rerank-mm行业方案&#xff1a;文旅部门景区图片库按游客搜索词智能排序 1. 项目背景与价值 文旅部门的景区图片库通常包含成千上万张照片&#xff0c;从自然风光到人文景观&#xff0c;从特色建筑到文化活动。当游客通过搜索词查找图片时&#xff0c;如何快速找到最相…...

NEURAL MASK 开发避坑指南:解决部署中常见的403 Forbidden等网络问题

NEURAL MASK 开发避坑指南&#xff1a;解决部署中常见的403 Forbidden等网络问题 最近在星图GPU平台上折腾NEURAL MASK服务&#xff0c;发现不少朋友在部署和调用时&#xff0c;总会遇到一些让人头疼的网络和权限问题。比如镜像死活拉不下来&#xff0c;或者好不容易部署好了&…...

OpenClaw电商运营助手:Qwen2.5-VL-7B批量生成商品图文详情

OpenClaw电商运营助手&#xff1a;Qwen2.5-VL-7B批量生成商品图文详情 1. 为什么需要自动化商品详情生成 每次大促前&#xff0c;运营团队最头疼的就是商品详情页的批量更新。去年双十一前&#xff0c;我手动处理了200多个SKU的图文优化&#xff0c;连续加班一周后&#xff0…...

FlutterApp豆瓣电影模块:复杂列表与详情页性能优化全指南

FlutterApp豆瓣电影模块&#xff1a;复杂列表与详情页性能优化全指南 【免费下载链接】flutter_app &#x1f525;&#x1f525;&#x1f525;本项目包括各种基本控件使用&#xff08;Text、TextField、Icon、Image、Listview、Gridview、Picker、Stepper、Dialog、Slider、Row…...

宇树A1电机折腾笔记

文章目录电脑SDK控制变态的硬件接线环境配置下位机直接控制上图就是笨笨的宇树A1&#xff0c;这是我目前为止转过的最难转的电机。电机的说明书、SDK链接都来自MATH-286-Pro的视频提供&#xff1a;宇树A1相关资料、宇树官方SDK仓库。这篇笔记分两部分&#xff0c;先使用SDK驱动…...

Jimeng LoRA惊艳效果:同一LoRA版本在不同seed下风格稳定性测评

Jimeng LoRA惊艳效果&#xff1a;同一LoRA版本在不同seed下风格稳定性测评 1. 项目简介 今天我们来聊聊一个很有意思的话题&#xff1a;同一个LoRA模型&#xff0c;用不同的随机种子&#xff08;seed&#xff09;生成图片&#xff0c;它的风格到底稳不稳定&#xff1f; 为了…...

Java学习——数据类型

目录 一、概述 二、基本数据类型 1、数值型 2、字符型 3、布尔型 三、引用数据类&#xff08;后期补充&#xff09; 1、类 2、接口 3、数组 4、枚举 5、注解 四、数据类型转换 1、概述 2、隐式转换&#xff08;自动类型转换&#xff09; 3、显式转换&#xff08…...

Intv_ai_mk11 C++高性能集成开发教程

Intv_ai_mk11 C高性能集成开发教程 1. 为什么需要高性能C集成方案 在AI应用开发中&#xff0c;性能往往是关键瓶颈。当你的C应用需要频繁调用AI模型API时&#xff0c;一个高效的集成方案能带来显著差异。想象一下&#xff0c;你正在开发一个实时视频分析系统&#xff0c;每秒…...

AudioSeal保姆级教学:Gradio界面多文件批量上传与异步检测队列设置

AudioSeal保姆级教学&#xff1a;Gradio界面多文件批量上传与异步检测队列设置 1. 引言 你是不是遇到过这样的场景&#xff1f;手里有一堆音频文件&#xff0c;需要挨个检查它们是不是AI生成的&#xff0c;或者想给一批音频文件批量加上水印。手动操作不仅效率低&#xff0c;…...

MAI-UI-8B应用案例:医疗登记表智能填充实战

MAI-UI-8B应用案例&#xff1a;医疗登记表智能填充实战 1. 医疗表单处理的痛点与解决方案 在医疗信息化系统中&#xff0c;患者登记表是每个医疗机构每天都要处理的基础文档。传统方式下&#xff0c;医护人员需要手动填写大量重复信息&#xff0c;不仅效率低下&#xff0c;还…...