Aop实现公共字段自动填充

文章目录

    • 1、问题:
      • 2.常规冗余不利于维护操作:
      • 3.实现思路:
    • 2、步骤:搭建一个简单的SpringBoot+MyPlus项目:
      • 表结构:
      • 1. 依赖:
      • 2. 配置连接信息:
      • 3. 创建三层架构:
      • 4. 代码开发:
        • 4.1 **定义数据库操作类型枚举**
        • 4.2、**自定义注解 AutoFill**
        • 4.3、**自定义切面,实现公共字段自动填充处理逻辑**
        • 4.4 mapper方法上面添加相应注解,和操作类型
        • 4.5 mapper.xml 文件:
        • 4.6. 测试:
          • 新增:
          • 修改:

1、问题:

在这里插入图片描述

2.常规冗余不利于维护操作:

而针对于这些字段,我们的赋值方式为:

1). 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户姓名。

2). 在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户姓名。

目前,在我们的项目中处理这些字段都是在每一个业务方法中进行赋值操作,如下:

在这里插入图片描述

​ 如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业务方法中进行操作, 编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?

答案是可以的,我们使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。

3.实现思路:

​ 在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:

在这里插入图片描述

  • 自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法
  • 自定义切面类AutoFillAspect,统一拦截加入了AutoFill 注解的方法,通过反射为公共字段赋值
  • 在 Mapper 的方法上加入 AutoFill 注解

2、步骤:搭建一个简单的SpringBoot+MyPlus项目:

表结构:

CREATE TABLE `user` (`id` bigint(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL COMMENT '学生名称',`age` int(10) DEFAULT NULL COMMENT '学生年龄',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`create_name` varchar(255) DEFAULT NULL COMMENT '创建人姓名',`update_time` datetime DEFAULT NULL COMMENT '修改时间',`update_name` varchar(255) DEFAULT NULL COMMENT '修改人姓名',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

在这里插入图片描述

1. 依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.js</groupId><artifactId>AopMybatisTest</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.5</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>1.8</java.version><log4j.version>1.2.17</log4j.version><druid.version>1.2.8</druid.version><mybatisplus.version>3.4.2</mybatisplus.version></properties><dependencies><!--web启动依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Aop--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--mybatis plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.version}</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><!--log4j--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

2. 配置连接信息:

#配置端口
server:port: 80spring:#配置数据源datasource:#配置数据源类型type: com.zaxxer.hikari.HikariDataSource#配置连接数据库的信息driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=falseusername: rootpassword: root#MyBatis-Plus相关配置
mybatis-plus:configuration:#配置日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3. 创建三层架构:

略~

4. 代码开发:

4.1 定义数据库操作类型枚举
package com.js.Enum;/*** 数据库操作类型*/
public enum OperationType {/*** 更新操作*/UPDATE,/*** 插入操作*/INSERT
}
4.2、自定义注解 AutoFill
package com.js.annotation;import com.js.Enum.OperationType;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解,用于标识某个方法需要进行功能字段自动填充处理*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {//数据库操作类型:UPDATE INSERTOperationType value();
}
4.3、自定义切面,实现公共字段自动填充处理逻辑
package com.js.constant;/*** @ClassName AutoFillConstant* @Author lenovo* @Date 2024-04-02 23:00* @Version 1.0**/
public class AutoFillConstant {public static final  String SET_CREATE_TIME = "setCreateTime";public static final  String SET_CREATE_NAME = "setCreateName";public static final  String SET_UPDATE_TIME = "setUpdateTime";public static final  String SET_UPDATE_NAME = "setUpdateName";
}
package com.js.aspect;import com.js.Enum.OperationType;
import com.js.annotation.AutoFill;
import com.js.constant.AutoFillConstant;
import com.sun.prism.impl.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;/*** 自定义切面,实现公共字段自动填充处理逻辑*/
@Aspect//声明这个类是一个切面类
@Component//他也是个Bean 也需要交个Spring进行管理
@Slf4j
public class AutoFillAspect {/*** 切入点:对那些类,那些方法进行拦截* 在切入点表达式里:如果想要缩小Spring的扫描范围,可以使用多切入点表达式连接 连接符 &&进行条件组合*  “ * ”:返回值是所有的*  “ com.js.mapper.*.*(..) ”:拦截这个mapper文件夹下面的所有类.方法(参数类型)*  “@annotation(com.js.annotation.AutoFill)” :拦截com.js.mapper中方法加了这个注解的方法*  。*/@Pointcut("execution(* com.js.mapper.*.*(..)) && @annotation(com.js.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知,在通知中进行公共字段的赋值* “joinPoint” :连接点,知道那个被拦截到的方法具体的参数是啥样的*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段自动填充...");/*** Java里如果要调用一个方法,有两种方式*   方式1:对象名.方法名(实参)*   方式2:反射调用方法*       先获取目标类的字节码*       在获取想要调用的方法*       最后反射执行这个方法*///1.获取到当前被拦截的方法上的数据库操作类型MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象OperationType operationType = autoFill.value();//获得数据库操作类型:新增,还是修改//2.获取所有添加注解并且被拦截的方法所有的参数--实体对象/*** 注意:*   如果想实现自动填充的话,一定要保证方法中参数的实体对象 Name Name,放在第一个位置,接下来我们要获取的话,*   就获取第一个就行,不管之后有多少参数,我们就获取第一个就行,这就是我们的约定*/// 为了提高程序的健壮性,加一个判断:如果方法没有实参,就什么都不做Object[] args = joinPoint.getArgs();if(args == null || args.length == 0){return;}Object entity = args[0];//3.准备赋值的数据LocalDateTime now = LocalDateTime.now();//---创建人信息:一般从ThreadLocal中获取,这里就给个默认值String currentId = "张三";//新增String updateId = "丰奶大硕";//修改//根据当前不同的操作类型,为对应的属性通过反射来赋值if(operationType == OperationType.INSERT){//新增操作//为4个公共字段赋值try {Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateName = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_NAME, String.class);//通过反射为对象属性赋值setCreateTime.invoke(entity,now);setCreateName.invoke(entity,currentId);} catch (Exception e) {e.printStackTrace();}}else if(operationType == OperationType.UPDATE){//为2个公共字段赋值try {Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateName = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_NAME, String.class);//通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateName.invoke(entity,updateId);} catch (Exception e) {e.printStackTrace();}}}
}
4.4 mapper方法上面添加相应注解,和操作类型
package com.js.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.js.Enum.OperationType;
import com.js.annotation.AutoFill;
import com.js.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;/*** @description: 用户mapper* @author:js* @date:2024/1/17 14:55* @version:3.0*/@Mapper
public interface UserMapper extends BaseMapper<User> {/*** 插入数据** @param user* @return*/@Insert("insert into User( name, age , create_time, update_time, create_name, update_name)" +" VALUES" +" (#{name}, #{age}, #{createTime}, #{updateTime}, #{createName}, #{updateName})")@AutoFill(value = OperationType.INSERT)int insert(User user);/*** 根据id修改分类* @param User*/@AutoFill(value = OperationType.UPDATE)void updated( @Param("user") User user);
}
4.5 mapper.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.js.mapper.UserMapper"><update id="updated">update userset name=#{user.name},age=#{user.age},update_time=#{user.updateTime},update_name=#{user.updateName}whereid=#{user.id}</update>
</mapper>
4.6. 测试:
新增:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

修改:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

css:阴影效果box-shadow

属性 box-shadow 属性值由四个参数组成&#xff1a; 水平偏移量&#xff1a;表示阴影相对于元素的水平位置。垂直偏移量&#xff1a;表示阴影相对于元素的垂直位置。模糊度&#xff1a;表示阴影的模糊程度。颜色&#xff1a;表示阴影的颜色 示例 单个box-shadow 0px -2px 6p…

MySQL之索引详细总结

索引简介 索引是帮助MySQL高效获取数据的数据结构(有序)。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用(指向)数据&#xff0c;这样就可以在这些数据结构上实现高级查法&#xff0c;这种数据结构就是索引 为什…

基于rip环境下的MGRE综合实验

实验要求 1、R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有IP地址。 2、&#xff08;1&#xff09;R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方。 &#xff08;2&#xff09;R2与R5之间使用ppp的CHAP认证&#xff0c;R5为主认证方。 &#…

YoloV8改进策略:Neck改进|GCNet(独家原创)|附结构图

摘要 本文使用GCNet注意力改进YoloV8,在YoloV8的Neck中加入GCNet实现涨点。改进方法简单易用&#xff0c;欢迎大家使用&#xff01; 论文:《GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond》 非局部网络&#xff08;NLNet&#xff09;通过为每个查…

洛谷 P8662 [蓝桥杯 2018 省 AB] 全球变暖

题目描述 你有一张某海域 &#xfffd;&#xfffd;NN 像素的照片&#xff0c;. 表示海洋、 # 表示陆地&#xff0c;如下所示&#xff1a; ....... .##.... .##.... ....##. ..####. ...###. .......其中 "上下左右" 四个方向上连在一起的一片陆地组成一座岛屿。例…

软件设计原则:依赖倒置

定义 依赖倒置原则&#xff08;Dependency Inversion Principle, DIP&#xff09;是面向对象设计原则之一&#xff0c;其核心是高层模块&#xff08;如业务逻辑&#xff09;不应当依赖于低层模块&#xff08;如具体的数据访问或设备控制实现&#xff09;&#xff0c;而是双方都…

ElasticSearch7.8的下载与安装和Kibana 7.8.0工具使用安装

1、ElasticSearch7.8.0下载 elasticsearch: 官方下载地址&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch 链接: https://pan.baidu.com/s/1wAKQoB3nhLhcnBlPfVOLxQ 提取码: t83n kibana: 链接: https://pan.baidu.com/s/156aD9zDdvUv8LFgDEIPoSw 提取码:…

PSCAD的使用

1. Canvas Settings中设置母线电压监测和虚拟连线十分重要。 2. Project Settings中snapshot可以实现分段仿真。 3. .out文献导出数据

操作系统—读者-写者问题及Peterson算法实现

文章目录 I.读者-写者问题1.读者-写者问题和分析2.读者—写者问题基本解法3.饥饿现象和解决方案总结 II.Peterson算法实现1.Peterson算法问题与分析(1).如何无锁访问临界区呢&#xff1f;(2).Peterson算法的基本逻辑(3).写对方/自己进程号的区别是&#xff1f; 2.只包含意向的解…

开发Vue组件库

使用脚手架新建vue项目&#xff08;以button 组件为例&#xff09; 1.新建package 文件夹&#xff0c;添加index.js 、custombutton 文件夹&#xff0c;custombutton 中 添加 custombutton .vue index.js <template><div>button</div> </template><…

图神经网络实战(7)——图卷积网络(Graph Convolutional Network, GCN)详解与实现

图神经网络实战&#xff08;7&#xff09;——图卷积网络详解与实现 前言1. 图卷积层2. 比较 GCN 和 GNN2.1 数据集分析2.2 实现 GCN 架构 小结系列链接 前言 图卷积网络 (Graph Convolutional Network, GCN) 架构由 Kipf 和 Welling 于 2017 年提出&#xff0c;其理念是创建一…

基于springboot+vue+Mysql的教学视频点播系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

idea使用docker将Java项目生成镜像并使用

1&#xff1a;开启docker 远程访问 使用 vim 编辑docker服务配置文件 vim /lib/systemd/system/docker.service [Service] Typenotify # the default is not to use systemd for cgroups because the delegate issues still # exists and systemd currently does not suppor…

软件设计SOLID原则

单一职责原则&#xff08;Single Responsibility Principle, SRP&#xff09; 每个类应该只有一个改变的理由&#xff0c;即一个类只负责一个功能领域中的相应职责。 开闭原则&#xff08;Open-Closed Principle, OCP&#xff09; 软件实体&#xff08;类、模块、函数等&#…

pip/conda导出或导入环境

目录 一、pip Option1: pip freeze 导出环境 导入环境 Option2: pipreqs工具 导出环境 导入环境 二、conda Option1: requirements.txt 导出环境 导入环境 Option2: myenv.yml 导出环境 导入环境 Python提供了强大的模块功能&#xff0c;能够方便开发者更加易于…

redis-cli通过DUMP和SADD进行redis set数据类型迁移

1、DUMP指定key导出 redis-cli -h redis1.aliyuncs.com -n 1 -a abc DUMP SOL_addresses Warning: Using a password with -a or -u option on the command line interface may not be safe. "\x02A#,7tC9xSy8FZ9idnDb3e8DaXH6mQYLDWzaDovvQmYESjCQ,DCbNevBcwToUArdEDx3Q7…

思科交换机配置指引(包含安全配置部分)以 Cisco WS-C3850-48T 配置为例

思科Catalyst 3850系列交换机: 型号: Cisco WS-C3850-48T端口数: 48个10/100/1000Mbps数据端口供电方式:AC电源(支持PoE+)堆叠技术:Cisco StackWise-480,提供480Gbps的堆叠吞吐量网络模块:支持多种可选上行链路模块,包括千兆位以太网、万兆位以太网和40千兆位以太网端口…

操作系统内功篇:内存管理之虚拟内存

一 虚拟内存 在这种情况下&#xff0c;要想在内存中同时运行两个程序是不可能的。如果第一个程序在 2000 的位置写入一个新的值&#xff0c;将会擦掉第二个程序存放在相同位置上的所有内容&#xff0c;所以同时运行两个程序是根本行不通的&#xff0c;这两个程序会立刻崩溃。 …

如何在 Ubuntu 12.04 上添加交换空间

本文涵盖的 Ubuntu 版本已不再受支持。如果您目前正在运行 Ubuntu 12.04 服务器&#xff0c;我们强烈建议升级或迁移到受支持的 Ubuntu 版本&#xff1a; 升级到 Ubuntu 14.04从 Ubuntu 14.04 升级到 Ubuntu 16.04将服务器数据迁移到受支持的版本 原因&#xff1a; Ubuntu 12…

亚远景科技-ASPICE评估目的

ASPICE评估是在特定目的的各种案例中进行的。一般来说&#xff0c;过程评估的目的是了解与评估组织单位实施的过程。 具体来说&#xff0c;根据 [ISO/IEC 33001-3.2.6] 中的定义&#xff0c;评估目的是&#xff1a; “一种声明&#xff0c;它作为评估输入信息中的一部分&#…