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

SpringAOP 面向切面编程

** Spring有两大核心特性:IOC(控制反转) 和 AOP(面向切面编程),但是 相比IOC在日常工作中的广泛应用,AOP却常常做了冷板凳,下面我从工作中的场景为大家打开AOP面向切面编程的大门。**

什么是AOP?

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

下面用一张图理解一下AOP:

在这里插入图片描述

可以看到,通过AOP技术,我们可以通过将业务中的通用部分抽取出来,并对业务进行加强,而我们只需要关心具体的业务逻辑即可。

下面是AOP相关的一些概念:

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

  • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

  • Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

  • Target(目标对象):织入 Advice 的目标对象.。

  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

那么在工作中,AOP具体怎么用呢?

场景1: 接口限流

背景:假设我们需要对一些接口进行限流,要求同一个用户指定时间内不可重复请求。

代码实现:

  1. 首先在类上面添加@Aspect标识这是一个切面类 并添加@Component注解让其被Spring发现

    @Slf4j
    @Aspect
    @Component
    public class RateLimitingAspect {
    
  2. 创建切面方法,并定义增强类型及切入点

    @Around("@annotation(com.beansmile.common.config.RateLimiting)")public Object rateLimiting(ProceedingJoinPoint pjp) throws Throwable {
    

    其中@Around代表增强类型为环绕增强、除此之外还有:before(执行前) after(执行后),注解的内容是设置的切入点,这里设置的为添加@RateLimiting注解的地方,此处也可以通过表达式设置为 xxx包下的类、xxx类下方法等等

  3. 创建自定义注解: RateLimiting

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface RateLimiting {/*** 间隔时间,单位秒*/int limitInSeconds() default 2;/*** 返回信息*/String returnMsg() default "您的请求太快了,请稍后提交!";
    }
    
  4. 具体逻辑代码

        @Around("@annotation(com.beansmile.common.config.RateLimiting)")public Object rateLimiting(ProceedingJoinPoint pjp) throws Throwable {String method = pjp.getSignature().toShortString();// lockKey = userId:xxxController.xxxMethod()String lockKey = AuthUtil.getUserId() + ":" + method;MethodSignature methodSignature = (MethodSignature) pjp.getSignature();Method signatureMethod = methodSignature.getMethod();// 拿到注解内容RateLimiting rateLimitingAnnotation = signatureMethod.getAnnotation(RateLimiting.class);String returnMsg = rateLimitingAnnotation.returnMsg();int limitInSeconds = rateLimitingAnnotation.limitInSeconds();Boolean lock = redisTemplate.opsForValue().setIfAbsent(method, 1, limitInSeconds, TimeUnit.SECONDS);if (BooleanUtils.isTrue(lock)) {// 放行return pjp.proceed();} else {log.info("请求限流,lockKey:{},间隔时间:{}", lockKey, limitInSeconds);throw new ServiceException(returnMsg);}}
    

上述代码中,我们以 userId + 类名称 + 方法名称 作为唯一标识,通过redis锁的方式进行限流。

测试代码:  这里我们设置了请求间隔为1秒
    @RequestMapping(value = "/test", method = RequestMethod.GET)@RateLimiting(limitInSeconds = 1)public Result<?> test() {// 具体业务逻辑// xxxxxxxxxxxxxreturn Result.OK("操作成功!");}

正常请求:

在这里插入图片描述

快速请求:

在这里插入图片描述

可以看到,我们通过使用AOP的方式实现了 接口限流的逻辑,并且没有修改任何业务逻辑。

场景2: 全局异常捕获

背景:在程序运行时不可避免的会出现一些异常, 如NullPointerException空指针、ArrayIndexOutOfBoundsException数组下标越界等, 这些异常直接抛到前端页面会导致用户体验不好,过多的try/catch捕捉又会导致代码冗余。

代码实现:

  1. 定义切入点
	@Pointcut("execution(public * com.beansmile.modules..*.*Controller.*(..)))")public void exceptionPointcut() {}

这里定义的切入点为 com.beansmile.modules包下 所有控制器下的方法。

  1. 创建异常枚举类
@Getter
@AllArgsConstructor
public enum ExceptionEnum {/*** NullPointerException*/NULL_POINTER(10001, NullPointerException.class, "空指针异常"),/*** ArrayIndexOutOfBoundsException*/ARRAY_INDEX_OUT_OF_BOUND(10002, ArrayIndexOutOfBoundsException.class, "数组下标越界"),/*** IOException*/IO(10003, IOException.class, "输入输出异常");final int code;final Class<?> clazz;final String name;public static ExceptionEnum getEnumByClazz(Class<?> clazz) {for (ExceptionEnum exceptionEnum : values()) {if (clazz.isAssignableFrom(exceptionEnum.clazz)) {return exceptionEnum;}}return null;}
}
  1. 具体逻辑代码
	@Around("exceptionPointcut()")public Object exceptionHandling(ProceedingJoinPoint pjp) throws Throwable {try {return pjp.proceed();} catch (Exception e) {// 通过枚举类获取异常信息ExceptionEnum exceptionEnum = ExceptionEnum.getEnumByClazz(e.getClass());if (null == exceptionEnum) throw e;// 打印异常信息printExceptionInfo(pjp, e);// 抛出自定义异常,并携带错误码用于快速排查。throw new JeecgBootException("网络开小差了,请稍后重试或联系客服![" + exceptionEnum.getCode() + "]");}}private void printExceptionInfo(ProceedingJoinPoint pjp, Exception e) {String methodName = pjp.getSignature().getName();String className = pjp.getTarget().getClass().getSimpleName();log.error("printExceptionInfo_类名:{},方法名:{},异常信息:{}", className, methodName, e.toString());e.printStackTrace();}

上述代码中,通过对异常时进行切入,对异常做了统一抛出和打印日志。

测试代码

    @RequestMapping(value = "/test", method = RequestMethod.GET)public Result<?> test() {// 模拟空指针异常String str = null;str.length();return Result.OK("操作成功!");}

请求接口进行测试:符合预期,统一错误返回,并携带错误码

在这里插入图片描述

查看后端日志: 保留了原始错误信息,并快速记录了错误的发生源头。

printExceptionInfo_类名:ApprovalFlowController,方法名:test,异常信息:java.lang.NullPointerException
java.lang.NullPointerException

相关文章:

SpringAOP 面向切面编程

** Spring有两大核心特性&#xff1a;IOC(控制反转) 和 AOP(面向切面编程)&#xff0c;但是 相比IOC在日常工作中的广泛应用&#xff0c;AOP却常常做了冷板凳&#xff0c;下面我从工作中的场景为大家打开AOP面向切面编程的大门。** 什么是AOP? 在软件业&#xff0c;AOP为Asp…...

灵办AI助手Chrome插件全面评测:PC Web端的智能办公利器

探索灵办AI助手在Mac OS上的高效表现&#xff0c;支持多款主流浏览器&#xff0c;助你轻松应对办公挑战 文章目录 探索灵办AI助手在Mac OS上的高效表现&#xff0c;支持多款主流浏览器&#xff0c;助你轻松应对办公挑战摘要引言开发环境介绍核心功能评测1. 网页翻译与双语对照 …...

Rancher 使用 Minio 备份 Longhorn 数据卷

0. 概述 Longhorn 支持备份到 NFS 或者 S3, 而 MinIO 就是符合 S3 的对象存储服务。通过 docker 部署 minio 服务&#xff0c;然后在 Longhorn UI 中配置备份服务即可。 1. MinIO 部署 1.1 创建备份目录 mkdir -p /home/longhorn-backup/minio/data mkdir -p /home/longhor…...

useRequest

用法 默认用法 第一参数是异步函数&#xff08;接口&#xff09;&#xff0c;在组件初次加载时&#xff0c;会自动触发该函数执行。 const { data, error, loading } useRequest(getUsername);第二个参数&#xff0c;是一个配置选项&#xff08;一个对象&#xff09; 详解…...

python动画:manim实现多面体的创建

一&#xff0c;介绍 内容 多面体&#xff08;discusses polyhedra&#xff09;&#xff0c;主要集中在一种称为多面体的几何形状类别&#xff0c;并突出介绍了五种柏拉图体&#xff08;Platonic solids&#xff09;&#xff0c;这些是具有特殊性质的多面体类型。 多面体 定义…...

数值计算引擎:搭建远程容器开发环境

Build VS Code Remote Docker Development Environment 大型CAE软件开发技术栈通常依赖多个第三方库&#xff0c;因此从零开始配置开发、编译、运行等环境通常较为繁琐。但随着公司的发展壮大&#xff0c;却经常需要为新加入的成员配备相应的开发环境&#xff1b;另外&#xf…...

二叉搜索树(Binary Search Tree)

1.二叉搜索树概念 二叉搜索树又称二叉排序树、二叉查找树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 1. 非空左子树的所有键值小于其根节点的键值 2. 非空右子树的所有键值大于其根节点的键值 3. 左右子树也分别为二叉搜索树 二叉搜索树一般不支持…...

Yii2框架的初始化及执行流程

当 Yii2 框架执行 index.php 入口脚本后&#xff0c;内部执行逻辑和顺序可以概括如下&#xff1a; 1、加载相关配置文件和关键组件&#xff1a; 加载 Composer 自动加载器&#xff1a; require DIR . ‘/…/vendor/autoload.php’; 加载 Yii 框架文件&#xff1a; require D…...

2024.1-2024.2pycharm无法打开terminal命令行

2024版的idea或pycharm打开terminal时会发生如下问题&#xff1a; Cannot open Windows PowerShell Failed to start [C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe,或 Cannot open Command Prompt Failed to start [C:\Windows\system32\cmd.exe] 需要点击标…...

50ETF期权移仓是什么?50ETF期权移仓要注意什么?

今天带你了解50ETF期权移仓是什么&#xff1f;50ETF期权移仓要注意什么&#xff1f;当前火热的期权交易市场&#xff0c;“移仓”同样是一门非常重要的技术。上证50ETF期权投资的过程中&#xff0c;我们可以进行一定的移仓操作的&#xff0c;如果移仓操作得好&#xff0c;可以很…...

软件工程概述(上)

1、软件的概念、特点和分类 要了解软件工程&#xff0c;首先让我们重新认识一下软件。如今可以说是一个软件定义一切的时代&#xff0c;虽然人工智能发展的如火如荼&#xff0c;但究其本质&#xff0c;核心还是软件。那么&#xff0c;如何给软件下一个定义呢&#xff1f;软件又…...

阿里云ubuntu系统安装mysql8.0

一、安装mysql8.0 1.已安装其他版本的mysql&#xff0c;需要删除 若没有不需要此操作 1 #卸载MySQL5.7版本 2 apt remove -y mysql-client5.7* mysql-community-server5.7* 4 # 卸载5.7的仓库信息 5 dpkg-l | grep mysql | awk iprint $2} | xargs dpkg -P2.更新仓库 apt u…...

自己搭建远程桌面服务器-RustDesk 极简版

linux搭建RustDesk保姆间教程_rustdesk linux-CSDN博客https://blog.csdn.net/yzs2022/article/details/135136491 背景 在某公司工作&#xff0c;向日葵等远程办公软件均已屏蔽&#xff0c;无法使用&#xff08;也没有明文规定不允许使用远程控制软件&#xff09;&#xff0c…...

数字资产是什么?怎么产生?怎么增长?

数字资产是什么&#xff1f; 数字资产是指企业或个人拥有或控制的&#xff0c;以电子数据形式存在的&#xff0c;在日常活动中持有以备出售或处于生产过程中的非货币性资产。它涵盖了广泛的范围&#xff0c;包括但不限于数字货币、数字证券、数字艺术品、虚拟土地等。这些资产…...

Centos7升级gitlab(17)

在 CentOS 7 中将 GitLab 从版本 17.1.1 升级到 17.2.2&#xff0c;涉及以下步骤。请务必在升级前备份数据&#xff0c;以防止升级过程中出现问题导致数据丢失。 升级步骤 1. 备份 GitLab 数据 在升级之前&#xff0c;确保已经备份了 GitLab 的数据&#xff0c;包括数据库、…...

Zookeeper详解以及常见的高可用关联组件

一、ZooKeeper 详解 Apache ZooKeeper 是一个开源的分布式协调服务&#xff0c;用于分布式应用程序之间的协调和管理。ZooKeeper 提供了一个高效、可靠的服务来帮助管理分布式系统中的共享配置信息、命名、同步和组服务等。 二、主要特性 1. 高可用性 ZooKeeper 集群通过选…...

Docker Containerd初体验

Docker Containerd概述 ​ Containerd是一个开源的容器运行时&#xff0c;它提供了一种标准化的方式来管理容器的生命周期。该项目最初是由Docker开发团队创建的&#xff0c;并在后来成为了一个独立的项目&#xff0c;被纳入了Cloud Native Computing Foundation&#xff08;C…...

开始使用 AWS SAM CLI

了解如何使用 AWS SAM CLI 在本地调试 lambda 函数 欢迎来到雲闪世界。我们将学习 AWS SAM CLI 的概念。SAM 是无服务器 应用程序 模型的缩写&#xff0c;是 Amazon Web Services 提供的一个框架&#xff0c;可以利用它在本地机器上构建应用程序并将其直接部署到 AWS Lambdas。…...

RK3588 RTL8125BG调试

RTL8125B是一款PCIE转RJ45的网卡控制器芯片&#xff0c;在底层调试时只需配置PCIE即可 diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi index 798359eaf061..d8a7a43cdfa0 100755 --- a/arch/arm64/bo…...

Python自省(机制与函数)

Python 自省&#xff08;Introspection&#xff09;是一种强大的特性&#xff0c;它允许程序在运行时检查对象的类型、属性以及它们如何相互关联。这种能力让 Python 非常适合于快速开发、调试以及编写需要高度动态交互的代码。Python 的自省机制主要通过内置的函数和类型来实现…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…...

云原生安全实战:API网关Envoy的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口&#xff0c;负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...