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实现策略
具体代理实现流程
- 定义切入点
- 横切逻辑
- 植入
在业务层执行不同的业务操作时,通过环绕切面在方法执行之前和之后做操作,通过切入点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切面编程
学习视频 一、定义模型:订单保存模型,订单更新模型,业务层,日志模型 订单保存模型 /*** author durunwu* date 2024-08-20-21:04*/ Data public class SaveOrder {private Long id; }订单更新模型 /*** author durunwu* date …...
目标检测 | yolov9 原理和介绍
相关系列: 目标检测 | yolov1 原理和介绍 目标检测 | yolov2/yolo9000 原理和介绍 目标检测 | yolov3 原理和介绍 目标检测 | yolov4 原理和介绍 目标检测 | yolov5 原理和介绍 目标检测 | yolov6 原理和介绍 目标检测 | yolov7 原理和介绍 目标检测 | yolov8 原理和…...
如何在不格式化的情况下解锁Android智能手机密码
如果您忘记了密码,您的 Android 移动设备会将您锁定。发生这种情况时,通常可以通过执行恢复出厂设置来重新获得对设备的访问权限。可悲的是,这将导致所有数据的丢失。下面列出了在不丢失任何个人数据的情况下解锁锁定的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…...
缓存实现方式
缓存是一个常见的话题,因为它对于提高应用程序性能至关重要。缓存是一种存储数据的临时地方,以便快速访问数据,减少对原始数据源(如数据库或文件系统)的访问次数,从而提高应用程序的响应速度和吞吐量。 Jav…...
鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射
关于中断部分系列篇将用三篇详细说明整个过程. 中断概念篇 中断概念很多,比如中断控制器,中断源,中断向量,中断共享,中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.用海公公打比方说明白中断各个概念…...
回归预测|基于雪消融优化相关向量机的数据回归预测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> 来设置超文本链接。 超链接可以是一个字,一个词,或者一组词,也可以是一幅图像,您可以点击这些内容来跳转到新的文档或者当前文档中的某个部分。 当您把鼠标指针移动到网页中的某个链接上时,箭…...
1.Java:集合
集合作用: 1.动态保存任意多个对象。 2.提供操作对象方法比如add,remove,set,get等方法。 3.使用集合添加,删除代码简洁。 集合分类 集合分为单列集合以及双列集合。 单列集合: 双列集合: Collection接口特点 1.Collection…...
C语言从头学49—文件操作(四)
本文继续上一篇有关 "文件操作" 内容并继续其编号。 十九、函数 ftell() 函数 ftell() 返回文件内部指针的当前位置,该函数原型定义在头文件 stdio.h中。 使用格式:ftell(参1); 参1:文件指针 返…...
算法力扣刷题记录 八十四【46.全排列】
前言 回溯章节第11篇。记录 八十四【46.全排列】 回溯学习过:组合问题、切割问题、子集问题。 本文是排列问题。 一、题目阅读 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入&…...
[C++进阶]map和set
一、关联式容器 STL中的部分容器,比如:vector、list、deque、forward_list(C11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。 那什么是关联式容器?它与序列式容器…...
ios机型下input输入框输入时拉高
问题 部分iphone或者ipad机型下,网页中使用input输入框时,调起ios软键盘输入时,input输入框被拉高,导致布局错位或者变化。 <input type"text" name"phonenum" >直观表现 注:在android机型…...
nacos 使用 docker 单机部署连接 MySQL 数据库并开启鉴权
文章目录 本地部署的配置启用鉴权(未验证) docker部署的配置修改docker 镜像源启用鉴权,必须添加如下环境变量如何生成鉴权的密钥 完整环境变量docker启动命令 本地部署的配置 文件结构 application.properties #配置文件 mysql-schema.sql …...
Opencv-C++笔记 (20) : 距离变换与分水岭的图像分割
文章目录 一、图片分割分水岭算法理解分水岭算法过程 二、距离变换与分水岭距离变换常见算法有两种分水岭变换常见的算法步骤 主要函数c代码四、结果展示 一、图片分割 图像分割(Image Segmentation)是图像处理最重要的处理手段之一 图像分割的目标是将图像中像素根据一定的规则…...
【流媒体】RTMPDump—Download(接收流媒体信息)
目录 RTMP协议相关: 【流媒体】RTMP协议概述 【流媒体】RTMP协议的数据格式 【流媒体】RTMP协议的消息类型 【流媒体】RTMPDump—主流程简单分析 【流媒体】RTMPDump—RTMP_Connect函数(握手、网络连接) 【流媒体】RTMPDump—RTMP_ConnectStr…...
Pytorch cat()与stack()函数详解
torch.cat() cat为concatenate的缩写,意思为拼接,torch.cat()函数一般是用于张量拼接使用的 cat(tensors: Union[Tuple[Tensor, ...], List[Tensor]], dim: _int 0, *, out: Optional[Tensor] None) -> Tensor: 可以看到cat()函数的参数…...
A. X(质因数分解+并查集)
题意:给定一个序列,求的方案数,其中,,i和j属于两个不同集合内。 解法:考虑怎样必须将某几个数放进一个集合里。如果数列中全是1,那么每个数都是独立的,也就是可以随便拿出这之中的数…...
自动化测试中如何应对网页弹窗的挑战!
在自动化测试中,网页弹窗的出现常常成为测试流程中的一个难点。无论是警告框、确认框、提示框,还是更复杂的模态对话框,都可能中断测试脚本的正常执行,导致测试结果的不确定性。本文将探讨几种有效的方法来应对网页弹窗的挑战&…...
Redission
一、Redis常见客户端 Jedis:简单,和命令最相似, API最丰富,多线程,不安全 SpringDataRedis: RedisTemplate,默认线程安全,底层基于Netty(异步支持),用于一…...
Realistic Vision V5.1虚拟摄影棚快速上手:新手3步生成比肩单反的人像
Realistic Vision V5.1虚拟摄影棚快速上手:新手3步生成比肩单反的人像 1. 为什么选择Realistic Vision V5.1虚拟摄影棚 如果你一直想尝试专业级人像摄影,但又苦于没有昂贵的单反设备和摄影棚,Realistic Vision V5.1虚拟摄影棚就是为你量身定…...
驱动管理工具:释放磁盘空间的开源解决方案
驱动管理工具:释放磁盘空间的开源解决方案 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 当你的系统频繁弹出磁盘空间不足警告,而C盘又找不到明显的大文件时&am…...
百度网盘提取码智能方案:从繁琐搜索到效率革命的技术跃迁
百度网盘提取码智能方案:从繁琐搜索到效率革命的技术跃迁 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 问题诊断:资源获取的现代困境 时间成本的指数级浪费 传统提取码查找流程涉及多平台切换、关键…...
08-Spring 数据访问 - JDBC 详解
08. Spring 数据访问 - JDBC 详解 8.1 Spring JDBC 概述 Spring JDBC 是 Spring Framework 提供的数据访问抽象层,简化了 JDBC 的使用,消除了样板代码,同时保留了 JDBC 的完整控制能力。 8.1.1 传统 JDBC 的问题 // 传统 JDBC 代码 - 大量样板代码 public List<User&…...
【RT-DETR涨点改进】TGRS 2026 | 全网独家创新、特征融合改进篇| 引入STSAM协同时空注意力融合模块,发论文热点创新,注意力能够互相引导强化边界和结构细节,增强目标检测高效涨点
一、本文介绍 🔥本文给大家介绍使用 STSAM协同时空注意力融合模块 改进RT-DETR网络模型,STSAM 是 空间域特征增强模块,通过全局跨时相注意力和局部坐标注意力的并行处理,能有效聚焦真实变化目标,强化边界和结构细节,同时兼顾训练稳定性,为后续浅层特征融合提供高质量特…...
手机号查QQ号终极指南:3分钟快速找回遗忘的QQ号码
手机号查QQ号终极指南:3分钟快速找回遗忘的QQ号码 【免费下载链接】phone2qq 项目地址: https://gitcode.com/gh_mirrors/ph/phone2qq 你是否曾因忘记QQ号而无法登录?是否因为更换手机需要重新绑定QQ却找不到账号信息?手机号查QQ号工…...
Next.js API路由的正确使用姿势
在使用Next.js开发应用时,API路由的配置和使用是非常重要的一部分。尤其是当我们从客户端组件中请求API时,如果不正确配置,可能会遇到一些常见的错误,比如404错误。本文将通过实例详细解释如何在Next.js中正确配置和使用API路由。 问题背景 假设你正在使用Next.js 14.2.3…...
golang如何实现零知识证明基础_golang零知识证明基础实现教程
Go 不内置零知识证明能力,需依赖第三方库;主流ZKP工具链绑定Rust/C/TS,Go生态缺乏生产级原生实现;crypto包仅提供基础原语,无法支撑ZKP所需多项式承诺、配对运算等高级密码操作。Go 本身不内置零知识证明(Z…...
快速验证模型服务:AutoGen Studio中连接vLLM部署的Qwen3-4B
快速验证模型服务:AutoGen Studio中连接vLLM部署的Qwen3-4B 1. 环境准备与快速部署 1.1 镜像启动与基础检查 首先确保已成功启动AutoGen Studio镜像,该镜像已预置vLLM部署的Qwen3-4B-Instruct-2507模型服务。验证模型服务是否正常运行: c…...
万象视界灵坛保姆级教程:Bright-Pixel UI下上传图片+输入神谕标签全流程
万象视界灵坛保姆级教程:Bright-Pixel UI下上传图片输入神谕标签全流程 1. 教程概述 万象视界灵坛是一款基于OpenAI CLIP技术的高级多模态智能感知平台,通过独特的Bright-Pixel UI设计,将复杂的图像语义分析转化为直观有趣的交互体验。本教…...
