当前位置: 首页 > 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测试

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

http://www.lryc.cn/news/428691.html

相关文章:

  • 目标检测 | yolov9 原理和介绍
  • 如何在不格式化的情况下解锁Android智能手机密码
  • ts语法、nvm的使用以及github访问速度
  • 缓存实现方式
  • 鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射
  • 回归预测|基于雪消融优化相关向量机的数据回归预测Matlab程序SAO-RVM 多特征输入单输出 SAO-RVM
  • 如何在HTML中创建链接?什么是CSS定位?什么是CSS优化?
  • 1.Java:集合
  • C语言从头学49—文件操作(四)
  • 算法力扣刷题记录 八十四【46.全排列】
  • [C++进阶]map和set
  • ios机型下input输入框输入时拉高
  • nacos 使用 docker 单机部署连接 MySQL 数据库并开启鉴权
  • Opencv-C++笔记 (20) : 距离变换与分水岭的图像分割
  • 【流媒体】RTMPDump—Download(接收流媒体信息)
  • Pytorch cat()与stack()函数详解
  • A. X(质因数分解+并查集)
  • 自动化测试中如何应对网页弹窗的挑战!
  • Redission
  • 负载均衡详解
  • Swift与UIKit:构建卓越用户界面的艺术
  • Spring 中ClassPathXmlApplicationContext
  • Springboot邮件发送:如何配置SMTP服务器?
  • 二叉树--堆
  • 【K8s】专题十二(2):Kubernetes 存储之 PersistentVolume
  • python3多个图片合成一个pdf文件,生产使用验证过
  • Stable Diffusion赋能“黑神话”——助力悟空走进AI奇幻世界
  • 微信小程序登陆
  • SQL - 存储过程
  • RabbitMQ环境搭建