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

Aop切面编程

学习视频

一、定义模型:订单保存模型,订单更新模型,业务层,日志模型

订单保存模型

/*** @author durunwu* @date 2024-08-20-21:04*/
@Data
public class SaveOrder {private Long id;
}

订单更新模型

/*** @author durunwu* @date 2024-08-20-21:05*/
@Data
public class UpdateOrder {private Long orderId;
}

Service业务层实现保存、更新等操作

/*** @author durunwu* @date 2024-08-20-21:03*/
@Service
public class OrderService {/*** 单纯加注解是没用的,需要定义切面*/@RecordOperate(desc = "保存订单",convert = SaveOrderConvert.class)public Boolean saveOrder(SaveOrder saveOrder){System.out.println("save order, orderId: " + saveOrder.getId());return true;}@RecordOperate(desc = "更新订单", convert = UpdateOrderConvert.class)public Boolean updateOrder(UpdateOrder updateOrder){System.out.println("update order, orderId: " + updateOrder.getOrderId());return true;}}

定义日志模型用来记录订单保存、更新信息

/*** @author durunwu* @date 2024-08-20-21:14*/
@Data
public class OperateLogDO {private Long orderId;private String desc;private String result;}

二、需求

在每次订单操作时,需要记录订单的日志信息,记录日志的操作都是一样的,我们可以提取出来作为公共方法提供使用
但是提取出来之后,怎样去记录不同订单类型的日志呢?
首先每个对象的类型不同、属性不同,难道需要通过判断入参的类型这样去实现吗?不够优雅
解决办法,使用aop切面

/*** @author durunwu* @date 2024-08-20-21:09*/
@SpringBootApplication
public class Application implements CommandLineRunner {@AutowiredOrderService orderService;public static void main(String[] args) {new SpringApplication(Application.class).run(args);}@Overridepublic void run(String... args) throws Exception {/*** 需求:* 需要在每次订单操作时,记录订单的日志信息* 记录日志这个操作都是一样的,需要提取出来单独处理* 但是提出出来之后,怎样去记录不同订单类型的日志呢?难道需要通过判断入参的类型进行校验然后转型吗* 解决办法,使用aop切面*///保存订单,id=1SaveOrder saveOrder = new SaveOrder();saveOrder.setId(1L);orderService.saveOrder(saveOrder);//更新订单 id=2UpdateOrder updateOrder = new UpdateOrder();updateOrder.setOrderId(2L);orderService.updateOrder(updateOrder);}
}

三、Aop实现策略

具体代理实现流程

  1. 定义切入点
  2. 横切逻辑
  3. 植入
    在业务层执行不同的业务操作时,通过环绕切面在方法执行之前和之后做操作,通过切入点ProceedingJoinPoint在方法执行之前通过反射获取到对应的方法签名MethodSignature,通过方法签名获取到当前注解@annotation,然后获取当前注解手动定义的类型Class,使用当前Class类型在Lambda泛化接口Convert< PARAM > 获取到对应日志模型实例,最后通过不同的业务模型去做具体的业务
    注意不推荐使用该操作,如果主业务发生了异常,切面逻辑是不能回滚的

1.Lambda泛化接口

抽象父接口

/*** @author durunwu* @date 2024-08-20-22:32*/
public interface Convert<PARAM> {/*** 通过不同的入参,转换成标准的日志模型*/OperateLogDO convertDurunwu(PARAM param);
}

子实现1:实现订单保存的日志模型

/*** @author durunwu* @date 2024-08-20-22:34*/
public class SaveOrderConvert implements Convert<SaveOrder>{@Overridepublic OperateLogDO convertDurunwu(SaveOrder saveOrder) {OperateLogDO operateLogDO = new OperateLogDO();operateLogDO.setOrderId(saveOrder.getId());return operateLogDO;}
}

子实现2:实现订单更新的日志模型

/*** @author durunwu* @date 2024-08-20-22:35*/
public class UpdateOrderConvert implements Convert<UpdateOrder>{@Overridepublic OperateLogDO convertDurunwu(UpdateOrder updateOrder) {OperateLogDO operateLogDO = new OperateLogDO();operateLogDO.setOrderId(updateOrder.getOrderId());return operateLogDO;}
}

2.定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RecordOperate {//定义每次操作的类型,默认不填String desc() default "";//定义一个类型,需要是Convert的子类Class<? extends Convert> convert();
}

3.切面逻辑

package com.durunwu.study.demos.oAuth2.daily.aop;import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;/*** 定义切面* @author durunwu* @date 2024-08-20-21:18*/
@Aspect
@Component
public class OperateAspect {/*** 不推荐,主业务发生了异常,切面逻辑不能回滚** 1.定义切入点,把注解RecordOperate的Class Path复制* 2.横切逻辑* 3.值入*///定义切面方法@Pointcut("@annotation(com.durunwu.study.demos.oAuth2.daily.aop.RecordOperate)")public void pointcut() {};//定义线程池private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));//使用环绕通知,定义横向逻辑@Around(value = "pointcut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//切入点ProceedingJoinPoint//拿到切入点的返回结果,这里如果抛异常则不记录流水,如果存在异常则抛出Object result = proceedingJoinPoint.proceed();//异步执行threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() {try {//通过反射拿到方法签名Signature signature = proceedingJoinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;//再通过方法签名拿到注解Method method = methodSignature.getMethod();//传入当前注解RecordOperateRecordOperate annotation = method.getAnnotation(RecordOperate.class);//使用注解的convert方法获取传入的类型Class<? extends Convert> convert = annotation.convert();//因为convert是一个类,需要实例化对象,获取对应的实现Convert logConvert = convert.newInstance();OperateLogDO operateLogDO = logConvert.convertDurunwu(proceedingJoinPoint.getArgs()[0]);//构造写入流水的模型,//OperateLogDO operateLogDO = new OperateLogDO();//operateLogDO.setOrderId();//获取注解上定义的操作类型operateLogDO.setDesc(annotation.desc());operateLogDO.setResult(result.toString());//怎么才能获取对应类型的业务id呢? 难道需要if判断是什么类型的对象再去取对应的id?这样不太优雅//解决思路:定义Lambda接口,通过不同的类型去转换,通过不同的入参去拿到一个不同的标准模型//定义Convert接口,入参泛化Convert<PARAM>System.out.println("isnert operateLog" + operateLogDO);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}});return result;}}

四、Debug测试

在这里插入图片描述在这里插入图片描述
控制台输出同步操作和异步操作
在这里插入图片描述

相关文章:

Aop切面编程

学习视频 一、定义模型&#xff1a;订单保存模型&#xff0c;订单更新模型&#xff0c;业务层&#xff0c;日志模型 订单保存模型 /*** author durunwu* date 2024-08-20-21:04*/ Data public class SaveOrder {private Long id; }订单更新模型 /*** author durunwu* date …...

目标检测 | yolov9 原理和介绍

相关系列&#xff1a; 目标检测 | yolov1 原理和介绍 目标检测 | yolov2/yolo9000 原理和介绍 目标检测 | yolov3 原理和介绍 目标检测 | yolov4 原理和介绍 目标检测 | yolov5 原理和介绍 目标检测 | yolov6 原理和介绍 目标检测 | yolov7 原理和介绍 目标检测 | yolov8 原理和…...

如何在不格式化的情况下解锁Android智能手机密码

如果您忘记了密码&#xff0c;您的 Android 移动设备会将您锁定。发生这种情况时&#xff0c;通常可以通过执行恢复出厂设置来重新获得对设备的访问权限。可悲的是&#xff0c;这将导致所有数据的丢失。下面列出了在不丢失任何个人数据的情况下解锁锁定的Android 手机的有效方法…...

ts语法、nvm的使用以及github访问速度

TS基础语法 let aa:string "123" let bb:number 123 let cc:boolean true let dd:undefined undefined let ee:null null let list:Array<string> ["1", 2, 3] let list2:string[] ["1", 2, 3]interface Ibj {name: string,age: n…...

缓存实现方式

缓存是一个常见的话题&#xff0c;因为它对于提高应用程序性能至关重要。缓存是一种存储数据的临时地方&#xff0c;以便快速访问数据&#xff0c;减少对原始数据源&#xff08;如数据库或文件系统&#xff09;的访问次数&#xff0c;从而提高应用程序的响应速度和吞吐量。 Jav…...

鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射

关于中断部分系列篇将用三篇详细说明整个过程. 中断概念篇 中断概念很多&#xff0c;比如中断控制器&#xff0c;中断源&#xff0c;中断向量&#xff0c;中断共享&#xff0c;中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.用海公公打比方说明白中断各个概念…...

回归预测|基于雪消融优化相关向量机的数据回归预测Matlab程序SAO-RVM 多特征输入单输出 SAO-RVM

回归预测|基于雪消融优化相关向量机的数据回归预测Matlab程序SAO-RVM 多特征输入单输出 SAO-RVM 文章目录 前言回归预测|基于雪消融优化相关向量机的数据回归预测Matlab程序SAO-RVM 多特征输入单输出 SAO-RVM 一、SAO-RVM模型1. 基本模型原理2. 贝叶斯框架3. 模型优化流程4. 总…...

如何在HTML中创建链接?什么是CSS定位?什么是CSS优化?

HTML使用标签 <a> 来设置超文本链接。 超链接可以是一个字&#xff0c;一个词&#xff0c;或者一组词&#xff0c;也可以是一幅图像&#xff0c;您可以点击这些内容来跳转到新的文档或者当前文档中的某个部分。 当您把鼠标指针移动到网页中的某个链接上时&#xff0c;箭…...

1.Java:集合

集合作用&#xff1a; 1.动态保存任意多个对象。 2.提供操作对象方法比如add,remove,set,get等方法。 3.使用集合添加&#xff0c;删除代码简洁。 集合分类 集合分为单列集合以及双列集合。 单列集合&#xff1a; 双列集合&#xff1a; Collection接口特点 1.Collection…...

C语言从头学49—文件操作(四)

本文继续上一篇有关 "文件操作" 内容并继续其编号。 十九、函数 ftell() 函数 ftell() 返回文件内部指针的当前位置&#xff0c;该函数原型定义在头文件 stdio.h中。 使用格式&#xff1a;ftell(参1); 参1&#xff1a;文件指针 返…...

算法力扣刷题记录 八十四【46.全排列】

前言 回溯章节第11篇。记录 八十四【46.全排列】 回溯学习过&#xff1a;组合问题、切割问题、子集问题。 本文是排列问题。 一、题目阅读 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&…...

[C++进阶]map和set

一、关联式容器 STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 那什么是关联式容器&#xff1f;它与序列式容器…...

ios机型下input输入框输入时拉高

问题 部分iphone或者ipad机型下&#xff0c;网页中使用input输入框时&#xff0c;调起ios软键盘输入时&#xff0c;input输入框被拉高&#xff0c;导致布局错位或者变化。 <input type"text" name"phonenum" >直观表现 注&#xff1a;在android机型…...

nacos 使用 docker 单机部署连接 MySQL 数据库并开启鉴权

文章目录 本地部署的配置启用鉴权(未验证) docker部署的配置修改docker 镜像源启用鉴权&#xff0c;必须添加如下环境变量如何生成鉴权的密钥 完整环境变量docker启动命令 本地部署的配置 文件结构 application.properties #配置文件 mysql-schema.sql …...

Opencv-C++笔记 (20) : 距离变换与分水岭的图像分割

文章目录 一、图片分割分水岭算法理解分水岭算法过程 二、距离变换与分水岭距离变换常见算法有两种分水岭变换常见的算法步骤 主要函数c代码四、结果展示 一、图片分割 图像分割(Image Segmentation)是图像处理最重要的处理手段之一 图像分割的目标是将图像中像素根据一定的规则…...

【流媒体】RTMPDump—Download(接收流媒体信息)

目录 RTMP协议相关&#xff1a; 【流媒体】RTMP协议概述 【流媒体】RTMP协议的数据格式 【流媒体】RTMP协议的消息类型 【流媒体】RTMPDump—主流程简单分析 【流媒体】RTMPDump—RTMP_Connect函数&#xff08;握手、网络连接&#xff09; 【流媒体】RTMPDump—RTMP_ConnectStr…...

Pytorch cat()与stack()函数详解

torch.cat() cat为concatenate的缩写&#xff0c;意思为拼接&#xff0c;torch.cat()函数一般是用于张量拼接使用的 cat(tensors: Union[Tuple[Tensor, ...], List[Tensor]], dim: _int 0, *, out: Optional[Tensor] None) -> Tensor: 可以看到cat()函数的参数&#xf…...

A. X(质因数分解+并查集)

题意&#xff1a;给定一个序列&#xff0c;求的方案数&#xff0c;其中&#xff0c;&#xff0c;i和j属于两个不同集合内。 解法&#xff1a;考虑怎样必须将某几个数放进一个集合里。如果数列中全是1&#xff0c;那么每个数都是独立的&#xff0c;也就是可以随便拿出这之中的数…...

自动化测试中如何应对网页弹窗的挑战!

在自动化测试中&#xff0c;网页弹窗的出现常常成为测试流程中的一个难点。无论是警告框、确认框、提示框&#xff0c;还是更复杂的模态对话框&#xff0c;都可能中断测试脚本的正常执行&#xff0c;导致测试结果的不确定性。本文将探讨几种有效的方法来应对网页弹窗的挑战&…...

Redission

一、Redis常见客户端 Jedis&#xff1a;简单&#xff0c;和命令最相似&#xff0c; API最丰富&#xff0c;多线程&#xff0c;不安全 SpringDataRedis: RedisTemplate&#xff0c;默认线程安全&#xff0c;底层基于Netty&#xff08;异步支持&#xff09;&#xff0c;用于一…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

从零开始了解数据采集(二十八)——制造业数字孪生

近年来&#xff0c;我国的工业领域正经历一场前所未有的数字化变革&#xff0c;从“双碳目标”到工业互联网平台的推广&#xff0c;国家政策和市场需求共同推动了制造业的升级。在这场变革中&#xff0c;数字孪生技术成为备受关注的关键工具&#xff0c;它不仅让企业“看见”设…...