Spring Aop实现日志收集和重复属性赋值

news/2024/10/8 10:18:37 标签: java, 开发语言

Spring Aop实现日志收集和重复属性赋值

简介

​ AOP(Aspect-Oriented Programming),即面向切面编程,用人话说就是把公共的逻辑抽出来,让开发者可以更专注于业务逻辑开发。

​ 和IOC一样,AOP也指的是一种思想。AOP思想是OOP(Object-Oriented Programming)的补充。OOP是面向类和对象的,但是AOP则是面向不同切面的。一个切面可以横跨多个类和对象去操作,极大的丰富了开发者的使用方式,提高了开发效率。

譬如,一个订单的创建,可能需要以下步骤:

  1. 权限校验

  2. 事务管理

  3. 创建订单

  4. 日志打印

如果使用AOP思想,我们就可以把这四步当成四个“切面”,让业务人员专注开发第三个切面,其他三个切面则是基础的通用逻辑,统一交给AOP封装和管理。

Spring AOP有如下概念:

术语翻译释义
Aspect切面切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。
PointCut切入点切入点是对连接点进行拦截的条件定义,决定通知应该作用于截哪些方法。(充当where角色,即在哪里做)
Advice通知通知定义了通过切入点拦截后,应该在连接点做什么,是切面的具体行为。(充当what角色,即做什么)
Target目标对象目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
JoinPoint连接点连接点是程序在运行时的执行点,这个点可以是正在执行的方法,或者是正在抛出的异常。因为Spring只支持方法类型的连接点,所以在Spring中连接点就是运行时刻被拦截到的方法。连接点由两个信息确定:+ 方法(表示程序执行点,即在哪个目标方法)+ 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
Weaving织入织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。

对于通知类型来说:

Before Advice连接点执行前执行的逻辑
After returning advice连接点正常执行(未抛出异常)后执行的逻辑
After throwing advice连接点抛出异常后执行的逻辑
After finally advice无论连接点是正常执行还是抛出异常,在连接点执行完毕后执行的逻辑
Around advice该通知可以非常灵活的在方法调用前后执行特定的逻辑

代码实现

1、创建注解

java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

2、切面类

java">
@Aspect
@Slf4j
@Component
public class LogAspect {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.zh.cn.*.*.*(..)) && @annotation(com.zh.cn.annotation.Log)")
    public void LogAspect(){

    }

    @Before("LogAspect()")
    public void before(JoinPoint joinPoint){
        //收集日志系统
        log.info("日志收集---前置通知");
        //获取日志收集中的实体类
        log.info("参数:{}",joinPoint);
        //获取basePojo实体类
        Object[] args = joinPoint.getArgs();
        Object entity = args[0];
        try {
            Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", Date.class);
            Method setUpdateName = entity.getClass().getDeclaredMethod("setUpdateName", String.class);
            //通过反射为对象赋值
            setUpdateTime.invoke(entity,new Date());
            setUpdateName.invoke(entity,"zh");
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        //收集日志
        //~~~~
        log.info("日志收集---后置通知");
    }
}

3、controller层

java">/**
 * @Description:
 * @author:<a href="2358853434@qq.com"></a> zh
 * @Create : 2024/10/5
 **/
@RestController
@RequestMapping("/order/v1")
public class OrderController {
    @Resource
    private OrderService orderService;

    @GetMapping("/update")
    public Result update() {
        Order order = new Order();
        order.setId(1292071939041697792L);
        order.setName("测试商品");
        order.setNum(128L);

        return orderService.update(order);
    }
    @GetMapping("/insert")
    public Result insert(){
        return  Result.success();
    }
}

4、service层

java">@Service
@Slf4j
public class OrderServiceimpl implements OrderService {
    @Resource
    RedisTemplate<String,String> redisTemplate;


    @Resource
    RedisDistributedLock redisDistributedLock;
    /**
     * 更新订单接口
     * @param order
     * @return
     */
    @Log
    public Result update(Order order) {
        log.info("update开始,订单号:{}",order.getId()+"order:"+order);
        //加锁成功
        while(redisDistributedLock.tryLock("order:"+order.getId()+":"+Thread.currentThread().getId(), "requestId", 10)) {
            //执行订单减减
            log.info("加锁成功"+Thread.currentThread().getId());
            redisTemplate.opsForValue().set("order:"+order.getId(), order.toString());
            break;
        }
        if (redisDistributedLock.unlock("order:"+order.getId()+":"+Thread.currentThread().getId(),"requestId")) {
            log.info("update结束,订单号:{}",order.getId());
            return Result.success();
        }
        return Result.error("下单失败");
    }
}

5、测试

测试用例

由于项目中加了spring-security的相关依赖,需要配置Authorization,不需要的友友请自行删除

GET http://localhost:19002/order/v1/update
Authorization: Basic cm9vdDpyb290
结果
测试用例

由于项目中加了spring-security的相关依赖,需要配置Authorization,不需要的友友请自行删除

GET http://localhost:19002/order/v1/update
Authorization: Basic cm9vdDpyb290
结果

image-20241005140727144


http://www.niftyadmin.cn/n/5694006.html

相关文章

优化理论及应用精解【25】

文章目录 优化学习率调度1. 阶梯衰减&#xff08;Step Decay&#xff09;2. 余弦退火&#xff08;Cosine Annealing&#xff09;3. 多项式衰减&#xff08;Polynomial Decay&#xff09;4. 指数衰减&#xff08;Exponential Decay&#xff09;总结 梯度弥散效应 参考文献 优化 …

R知识图谱1—tidyverse玩转数据处理120题

以下是本人依据张老师提供的tidyverse题库自行刷题后的tidyverse Rmd文件&#xff0c;部分解法参考张老师提示&#xff0c;部分解法我本人灵感提供 数据下载来源https://github.com/zhjx19/tidyverse120/tree/main/data 参考https://github.com/MaybeBio/R_cheatsheet/tree/mai…

【VUE】Vue2与Vue3两者Diff流程的区别

Vue2和Vue3在Diff算法的实现上有一些显著的区别&#xff0c;主要表现在以下几个方面&#xff1a; 源码架构&#xff1a;Vue2的Diff算法是在虚拟DOM模块中实现的&#xff0c;需与渲染模块和事件模块耦合在一起。而Vue3则将Diff算法单独抽离为一个模块&#xff0c;便于维护和重用…

selenium的webdriver常用方法和属性介绍(2)

selenium的webdriver介绍 从selenium导入webdriver模块&#xff0c;在pycharm中跳转webdriver模块的__init__.py文件&#xff0c;内容如图所示&#xff1a;从selenium包的子目录中导入了很多模块并做了重命名&#xff0c;用于支持如下 Chrome/Edge/Ie/Firefox/Safari浏览器。 使…

请解释一下数据库的分区和分片?请解释一下数据库的日志和日志的重要性?

请解释一下数据库的分区和分片&#xff1f; 数据库的分区和分片是两种用于提高数据库性能和可扩展性的技术&#xff0c;它们各自具有不同的特点和应用场景。以下是对这两种技术的详细解释&#xff1a; 一、数据库分区 定义&#xff1a; 数据库分区是将一个大型的数据库表或索…

QT-数据类型容器类窗口控件模态框

1. Qt 数据类型 1.1 数字类型 整型&#xff1a; qint8、qint16、qint32、qint64 无符号整型&#xff1a; quint8、quint16、quint32、quint64 qintptr: 指针类型 根据系统类型不同而不同&#xff0c;32位系统为qint32、64位系统为qint64 &#xff08;Linux&#xff09; 浮点…

IL2CPP和Mono的区别

Mono 是一种开源的跨平台 .NET 框架实现&#xff0c;能够执行 C# 代码。Unity 使用 Mono 来处理 C# 脚本&#xff0c;并通过 JIT&#xff08;Just-In-Time&#xff09;即时编译器将托管代码转换为本地机器代码&#xff0c;随后在目标平台上执行 IL2CPP 代表 Intermediate Lang…

【STM32 Blue Pill编程实例】-OLED显示DHT22传感器数据

OLED显示DHT22传感器数据 文章目录 OLED显示DHT22传感器数据1、DHT22介绍2、硬件准备与接线3、模块配置3.1 定时器配置3.2 DHT22引脚配置3.3 OLED配置4、代码实现在本文中,我们将介绍如何将 DHT22 温度和湿度传感器与 STM32 Blue Pill 开发板连接,并使用 HAL 库在 STM32CubeI…