AOP使用场景记录总结(缓慢补充更新中)

news2024/9/29 5:02:04

测试项目结构:

在这里插入图片描述

目前是测试两个日志记录和 代码的性能测试 后面如果有其他的应用场景了在添加.其实一中就包括了二,但是没事,多练一遍

1. 日志记录

比如说对service层中的所有增加,删除,修改方法添加日志, 记录内容包括操作的时间 操作的方法, 方法的参数, 方法所在的类, 方法执行的时间. 等进行日志记录.后面可以把数据存到数据库中,也可以把数据存到日志文件中这里就简单的做一个演示,直接输出到控制台了.

对于AOP有两种方式可以实现, 由于注解较为灵活,所以这里就使用注解的方式来实现.如果不太会使用注解的方式,可以看一下我之前的笔记.

SpringAop中的五种常见的通知的注解及@annotation 切入点表达式_yfs1024的博客-CSDN博客

第一步: 定义注解

这里因为是对方法切入,所以创建了一个MethodLog注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodLog {
}

第二步: 在对应的方法上添加注解 这里是对Service层所以就加到Service上了

类如下: 其中包括增删改查, 因为我们要求的是对增删改进行日志记录,所以就把注解加到增删改的方法上即可

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Override
    @MethodLog
    public void insertEmployee(Employee employee) {
        System.out.println("新增");
    }

    @Override
    @MethodLog
    public void deleteEmployee(Long EmployeeId) {
        System.out.println("删除");
    }

    @Override
    public List<Employee> list() {
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee());
        return employees;
    }

    @Override
    @MethodLog
    public void updateEmployee(Employee employee) {
        System.out.println("更新员工信息");
    }
}

第三步: 定义切面 定义切入点, 定义通知

@Aspect
@Component
@Slf4j
public class MyAspect {
    //  定义切入点 对那些方法进行切入
	// com.yfs1024.service任意子包下的任意类中任意参数,任意返回值   并且 使用@MethodLog注解的方法
    @Pointcut("execution(* com.yfs1024.service..*.*(..)) && @annotation(com.yfs1024.annocation.MethodLog)")
    public void pointCutForMethodLog() {
    }
    
    //  定义通知  做什么
    
        @Around("pointCutForMethodLog()")
    public Object methodLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //操作时间
        LocalDateTime operateTime = LocalDateTime.now();
        //操作类名
        String className = joinPoint.getTarget().getClass().getName();
        //操作方法名
        String methodName = joinPoint.getSignature().getName();
        //操作方法参数
        Object[] args = joinPoint.getArgs();
        String methodParams = Arrays.toString(args);
        long begin = System.currentTimeMillis();
		// 为了防止时间太短,睡会
        Thread.sleep(3);
        //调用原始目标方法运行
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();

        //操作耗时
        Long costTime = end - begin;
        log.info("方法操作时间{},方法所在类{},方法名{},方法的参数{},方法执行耗时			{}",operateTime,className,methodName,methodParams,costTime);
// 注意一定要返回
        return result;
    }
    
}

第四步: 测试

在测试类中进行测试, 注意因为我们使用了Spring的注解所以一定要在测试类上加上@SpringBootTest 默认会加但是还是要注意一下.

测试方法如下:

@SpringBootTest
class SpringAopApplicationTests {

    @Autowired
    private EmployeeService employeeService;
    
    @Test
    void methodLog() {
        Employee employee = new Employee();
        employeeService.insertEmployee(employee);
        employeeService.deleteEmployee(10001L);
        employeeService.list();
        employeeService.updateEmployee(employee);
    }
}

控制台结果:

在这里插入图片描述

嗯,真香 其实想想如果使用动态代理让我们自己写应该也能写出来这样的效果. 在之前的笔记中也有记录感兴趣的话可以看一下,在笔记的最后的案例中

试着让动态代理变得通俗易懂,通过三个案例_yfs1024的博客-CSDN博客

2. 性能测试.

需求: 现在有两个工具包, 分别是AliyunOssUtils 和 AliyunOssUtilsOld ,我们现在要通过AOP来对这两个工具包进行测试

你可能会问,就两个我还用AOP? 没错确实麻烦,但是这里就是举个例子. 后面如果多个的话我们只需要,修改切入点即可.

依然是上面的四步

第一步: 定义注解

// 性能测试日志记录
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PTLog {
}

第二步: 在对应的方法上添加注解 (也可以添加到,类上,但是其实还是执行方法的时候记录,因为有多个吗. 也可以是接口上,这样接口的实现类的所有的方法就都被切入了)

AliyunOssUtils

@Component
public class AliyunOssUtils {
    /**
     * 公共方法
     * @throws InterruptedException
     */
    @PTLog
    public void load(MultipartFile multipartFile) throws InterruptedException {
        Thread.sleep(10);
    }
}

AliyunOssUtilsOld

@Component
public class AliyunOssUtilsOld {
    /**
     * 公共方法
     * @throws InterruptedException
     */
    @PTLog
    public void load(MultipartFile multipartFile) throws InterruptedException {
        Thread.sleep(10);
    }
}

第三步: 定义切面 定义切入点, 定义通知

@Aspect
@Component
@Slf4j
public class MyAspect {   
    // com.yfs1024.utils任意子包下的任意类中任意参数,任意返回值   并且 使用@PTLog注解的方法
    @Pointcut("execution(* com.yfs1024.utils..*.*(..)) && @annotation(com.yfs1024.annocation.PTLog)")
    public void pointCutForPTLog() {}

    @Around("pointCutForPTLog()")
    public Object ptLog(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getName();
        long begin = System.currentTimeMillis();
//        为了防止时间太短,睡会
        Thread.sleep(3);
        //调用原始目标方法运行
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        Long costTime = end - begin;
        log.info("{}类中方法执行耗时{}",className,costTime);
        return result;
    }
}

第四步: 测试

@SpringBootTest
class SpringAopApplicationTests {
// 这里因为测试用, 所以上面的工具类上就加了@Component注解, 一般不加哈,其实也可以通过一个配置类配合@Bean注入到容器这里懒了,嘿
    @Autowired
    private AliyunOssUtils aliyunOssUtils;

    @Autowired
    private AliyunOssUtilsOld aliyunOssUtilsOld;

    @Test
    void myPTLog() throws InterruptedException {
        aliyunOssUtils.load();
        aliyunOssUtilsOld.load();
    }
}

控制台结果:

在这里插入图片描述

3.公共字段的填充

首先介绍一下什么是公共字段的填充, 我们在对数据进行更新操作的时候,或者插入操作的时候往往都需要记录一下操作的时间, 基本所有的表都会有这两个字段,即 更新日期, 创建日期, 另外我们的每张表还需要记录创建该条记录的人是谁,以及修改这条记录的是谁,等这些就叫做公共字段. 对于不同的表可能要求不同视情况而定, 可见我们对于公共字段的操作往往是在 对数据库的更新操作和插入操作. 所以我们可以定义一个枚举来表示这两个操作,以供后期的使用.

第0步:

public enum OperationType {
    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT
}

第一步: 定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoFill {
    OperationType value() default OperationType.INSERT;   // 设置枚举的参数,默认为插入操作
}

第二步: 在对应的方法上添加注解

这个时候就该想了, 我们把这些注解添在哪里合适?

答案是Mapper层, 因为Mapper层是和数据库交互的最后一个步骤, 我不管你之前怎么处理,知道在插入数据库之前把公共字段填充即可

代码如下:

@Mapper
public interface EmployeeMapper {

    /**
     * 根据用户名查询员工
     *
     * @param username
     * @return
     */
    @Select("select * from employee where username = #{username}")
    Employee getByUsername(String username);

    /**
     * @param employee
     */
//    @Options(useGeneratedKeys = true,keyProperty = "id")
    @AutoFill()
    void insertEmployee(Employee employee);

    /**
     * 根据条件查询用户列表
     *
     * @param employee
     * @return
     */
    Page<Employee> select(Employee employee);

    /**
     * 更新员工信息
     */
    @AutoFill(OperationType.UPDATE)
    void updateEmployee(Employee employee);
}

此时对插入和更新操作添加注解:

第三步: 定义切面 定义切入点, 定义通知

此时AOP的强大就体现的淋漓尽致

@Component
@Aspect
@Slf4j
public class AutoFillAspect {

    //    设置通知的连接点所在的位置
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annocation.AutoFill)")
    public void insertOrUpdateCommonsMeg() {
    }

    //    当前切入位置是mapper层,所以是在方法执行之前,即Service层执行之后,再给公共的属性赋值
    @Before("insertOrUpdateCommonsMeg()")
    public void beforeAutoFillCommonsField(JoinPoint joinPoint) throws Exception {
//        获取连接点的方法对象
        Object[] args = joinPoint.getArgs();
//        判断如果不为空,再执行操作,如果为空直接返回
        if (args == null || args.length == 0) {
            return;
        }
//        此时就可以获取参数列表中的第一个对象,即为当前需要添加数据的对象
        Object NowMethodObject = args[0];
        Class<?> obj = NowMethodObject.getClass();
//    判断当前的方法是Insert还是Update那些方法进行切入

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill annotation = signature.getMethod().getAnnotation(AutoFill.class);
//        获取当前的注解对象,通过value值进行判断
        if (annotation.value() == OperationType.INSERT) {
//            获取创建时间方法这个对象
            Method setCreateTime = obj.getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
//            获取设置创建者id的方法对象
            Method setCreateUser = obj.getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
//            调用传入参数
            setCreateTime.invoke(NowMethodObject, LocalDateTime.now());
            setCreateUser.invoke(NowMethodObject, BaseContext.getCurrentId());
        }
//        其次我要知道对谁切入
        Method setUpdateTime = obj.getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
//            获取设置创建者id的方法对象
        Method setUpdateTUser = obj.getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//            调用传入参数
        setUpdateTime.invoke(NowMethodObject, LocalDateTime.now());
        setUpdateTUser.invoke(NowMethodObject, BaseContext.getCurrentId());

    }
}

第四步: 测试

此时运行SpringBoot项目,进行更新和插入操作, 此时我们就可以看到数据库中的数据已经自动的填充进去

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS :autofill 如何覆盖浏览器自动填充表单的样式

CSS :autofill 如何覆盖浏览器自动填充表单的样式 :autofill 伪类匹配浏览器自动填充值的 input 元素. 如果用户继续编辑这个元素内容就会停止匹配. #name:autofill {background-color: red !important;border: 6px solid red; } #name:-webkit-autofill {background-color: …

OpenAI-ChatGPT最新官方接口《审核机制》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(七)(附源码)

Moderation 审核机制前言Introduction 导言Quickstart 快速开始其它资料下载ChatGPT 作为一个大型人工智能语言模型&#xff0c;在提供用户便捷交流的同时也承担着内容审核的责任。为了保护用户和社会免受不良信息的影响&#xff0c;ChatGPT 特别注重关于内容的审核。当用户发送…

UDS统一诊断服务【五】诊断仪在线0X3E服务

文章目录前言一、诊断仪在线服务介绍二、数据格式2.1&#xff0c;请求报文2.2&#xff0c;子功能2.3&#xff0c;响应报文前言 本文介绍UDS统一诊断服务的0X3E服务&#xff0c;希望能对你有所帮助 一、诊断仪在线服务介绍 诊断仪在线服务比较简单&#xff0c;其功能就是告诉服…

winForm目录文件介绍

先看项目结构 引用&#xff1a;添加引用&#xff0c;选择自己需要的程序集添加 app.config:配置文件 form1.cs&#xff1a;窗体文件&#xff0c;创建一个窗体所要具备的文件 program&#xff1a;程序入口点 再看创建项目后各个文件夹的含义 .sln:解决方案文件&#xff0c;…

网络模型-网络体系结构(OSI、TCP/IP)

网络模型&#xff08;网络体系结构&#xff09;网络模型网络的体系结构OSI模型TCP/IP模型OSI和TCP/IP模型对应关系图常见网络协议网络模型 网络的体系结构 1、网络采用分而治之的方法设计&#xff0c;将网络的功能划分为不同的模块&#xff0c;以分层的形式有机组合在一起。 …

智慧果园系统——以水肥一体化系统功能为基础实现智慧果园系统项目 需求文档

文章目录一、引言1.文档的作用2.文档的标准3.产品的范围二、综合描述1.项目前景2.项目目标3.项目功能4.调研和面谈A.硬数据采样a)硬数据分析的形式b)定量硬数据c)定性硬数据B.面谈a)第一次面谈&#xff1a;开放式问题b)第二次面谈&#xff1a;封闭式问题&#xff0b;开放性问题…

NIFI大数据进阶_离线同步MySql数据到HDFS_01_实际操作---大数据之Nifi工作笔记0029

然后我们实际操作一下如何把mysql中的数据同步到hdfs中去,这里注意,这里是查询mysql中的表中的数据,然后放到 hdfs中去,并不是说,如果mysql数据表中的数据变化了,就自动同步到hdfs,这个功能后面我们再说,这是增量同步 用到的是其他的处理器 首先我们创建一个处理器组mysqlto…

【行为型模式】迭代器模式

文章目录1、简介2、结构3、实现方式3.1、案例引入3.2、结构分析3.3、具体实现4、迭代器模式优缺点5、应用场景1、简介 迭代器模式(Iterator)是一种行为设计模式&#xff0c;它允许我们遍历一个复杂的集合对象而无需暴露其内部表示。它提供了一种统一的方式来访问一个聚合对象中…

在docker上安装MySQL和Redis

1. 通过docker命令下载mysql5.7镜像mysql5.7前期准备2. 通过docker命令下载mysql8.0镜像mysql8.0前期准备 3. 通过docker命令下载redis镜像redis前期准备 本文永久更新地址: 1. 通过docker命令下载mysql5.7镜像 mysql5.7前期准备 在Linux虚拟机上创建一个文件夹用来持久化数据…

replugin原理笔记

Replugin源码目录主要有4个工程组成&#xff0c;其组成如下图所示&#xff0c;包括2个gradle工程&#xff0c;2个Android library工程。 replugin-host-gradle replugin-host-library replugin-plugin-gradle replugin-plugin-library Replugin是一套完整的、稳定的、适合全面…

C++ 基础回顾(下)

C 基础回顾&#xff08;下&#xff09; 目录C 基础回顾&#xff08;下&#xff09;前言模板和泛型编程动态内存与数据结构动态内存数据结构继承与多态继承多态简单的输入输出工具与技术命名空间异常处理多重继承与虚继承时间和日期前言 C之前学过一点&#xff0c;但是很长时间…

进销存管理系统是什么?进销存管理系统优点?

库存管理不当导致物资浪费/过期/损坏&#xff0c;增加企业成本和风险&#xff1b; 无法有效监控销售和采购流程&#xff0c;交易的准确性和时效性不到位&#xff1b; 财务管理混乱&#xff1b; ...... 你是否遇到过以上问题&#xff1f; 进销存管理系统&#xff08;Inventory …

Softing FG-200——将FF H1现场总线集成到工业以太网

基金会现场总线FF&#xff08;FOUNDATION Fieldbus&#xff09;是专为过程自动化设计的通信协议&#xff0c;包含低速总线H1&#xff08;31.25kbits/s&#xff09;标准和高速以太网HSE&#xff08;High Speed Ethernet&#xff0c;100Mbits/s&#xff09;标准。FF H1主要针对于…

200左右蓝牙耳机有哪些推荐?质量好的平价蓝牙耳机分享

现在蓝牙耳机基本上都是人手必备的存在了&#xff0c;对比上千元的蓝牙耳机&#xff0c;两百左右价位蓝牙耳机才是更多人的优先选择、废话不多说&#xff0c;下面我就来为大家推荐几款200元上下&#xff0c;质量和口碑都好的蓝牙耳机&#xff0c;准备入手蓝牙耳机的小伙伴可以作…

Mac配置QT

Mac配置QT 前言&#xff1a; 系统版本&#xff1a;Ventura 13.2.1 (22D68) 先安装homebrew&#xff0c;参考&#xff1a; https://blog.csdn.net/ZCC361571217/article/details/127333754 Mac配置&#xff1a; 安装Qt与Qt Creator&#xff1a; 通过Homebrew安装(若没Homeb…

VL817S与之前其他型号的区别与改动

相对于VL817C0以及VL817B0来说&#xff0c;VL817S使用外部供电不需要接入5V&#xff0c;HUB 5V 请参考参考设计接地。内部3.3 LDO输出请悬空。1。2V LX和FB请悬空。如下所示&#xff0c;详见参考设计。 1、3.3V和1.2V之间的时序要求是怎么样的&#xff1f; 下图是VL817(S) 上电…

无线技术有哪些专业术语,看完本文=半个无线专家

无线技术是指通过无线电波或光波等无线传输媒介&#xff0c;实现信息、数据或信号的传递和通信的技术领域。在无线技术领域中&#xff0c;有许多专业术语用于描述和标识不同的技术和概念。 以下是常见的无线技术专业术语的简介&#xff1a; Wi-Fi&#xff08;无线局域网&#…

磁盘这列(Raid)

RAID介绍 RAID技术通过把多个硬盘设备组合成一个容量更大的、安全性更好的磁盘阵列。把数据切割成许多区段后分别放在不同的物理磁盘上&#xff0c;然后利用分散读写技术来提升磁盘阵列整体的性能&#xff0c;同时把多个重要数据的副本同步到不同的物理设备上&#xff0c;从而…

ImageNet使用方法(细节)自用!

学习记录&#xff0c;自用。 1. 下载数据集 点击以下链接下载种子文件&#xff0c;然后使用迅雷进行下载&#xff0c;仅下载勾选的文件即可。 https://hyper.ai/datasets/4889/c107755f6de25ba43c190f37dd0168dbd1c0877e 2. 解压 找到下载好的ILSVRC2012_img_train.tar 和…

移动端布局rem与vw的区别

目录 1. rem 2. rem的弊端与优点 3. rem布局前注意点 4. vw 5. vw单位和%单位对比 6. vw布局前注意点 7. vue项目中使用vw 1. rem 先简单说下rem&#xff0c;官当文档是这样说的&#xff1a; rem是css中的长度单位&#xff0c;1rem 根元素html的font-size值。当页面…