JavaWeb笔记整理14——公共字段自动填充技术实现

news/2024/10/4 20:18:22/文章来源:https://blog.csdn.net/2302_78946634/article/details/142032872

目录


为什么需要公共字段自动填充?

步骤1 自定义注解AutoFill

步骤2 自定义切面AutoFillAspect

步骤3 在Mapper接口的方法上加入AutoFill注解

@Before("autoFillPointCut()")

JoinPoint

你能通过 JoinPoint 获取哪些信息?

例子中的 JoinPoint

获取方法签名和注解

获取被拦截方法的参数

反射

什么是反射

获取 Class 对象

获取 Method 对象

动态调用方法——invoke()


为什么需要公共字段自动填充?

  1. 避免手动重复操作:每次插入、更新数据库时,都要手动设置 createTimeupdateTimecreateUserupdateUser 等字段,容易出现遗漏或不一致的问题。自动填充可以保证这些公共字段在插入或更新时自动赋值,无需手动干预。

  2. 提高代码的可维护性:如果在每个数据插入和更新的地方都写上手动的字段赋值代码,当需求变化时,需要逐一修改所有相关的代码。这不仅增加了维护成本,还增加了出错的几率。通过自动填充,这些字段可以集中管理,方便维护。

  3. 保证数据一致性:自动填充可以保证所有数据记录的时间戳和用户信息是一致的,并且可以通过统一的逻辑进行约束,减少人为错误带来的数据不一致问题。

步骤1 自定义注解AutoFill

/*** 数据库操作类型*/
public enum OperationType {/*** 更新操作*/UPDATE,/*** 插入操作*/INSERT}
/*** 自定义注解 用于表示某个方法需要进行功能字段自动填充处理*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {//数据库操作类型 update insertOperationType value();}

步骤2 自定义切面AutoFillAspect

/*** 自定义切面 实现公共字段自动填充处理逻辑*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知 在通知中进行公共字段的赋值*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段的填充...");//获取到当前被拦截的方法上的数据库操作类型MethodSignature signature =(MethodSignature)joinPoint.getSignature();//方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象OperationType operationType = autoFill.value();//获得数据库操作类型//获取当前被拦截到的方法参数——实体对象Object[] args = joinPoint.getArgs();if(args == null || args.length == 0){return;}Object entity = args[0];//准备赋值数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型 为属性赋值 通过反射if(operationType == OperationType.INSERT){//为四个公共字段赋值try {Method setCreatTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreatUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setCreatTime.invoke(entity,now);setCreatUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {throw new RuntimeException(e);}}else if(operationType == OperationType.UPDATE){//为2个公共字段赋值try {Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);//通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {throw new RuntimeException(e);}}}

步骤3 在Mapper接口的方法上加入AutoFill注解

 /*** 根据主键来动态修改属性* @param employee*/@AutoFill(value = OperationType.UPDATE)void update(Employee employee);/*** 插入员工数据* @param employee*/@Insert("insert into employee (name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user) " +"values (" +"#{name}, #{username}, #{password}, #{phone}, #{sex}, #{idNumber}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")@AutoFill(value = OperationType.INSERT)void insert(Employee employee);/*** 根据id修改分类* @param category*/@AutoFill(value = OperationType.UPDATE)void update(Category category);/*** 插入数据* @param category*/@AutoFill(value = OperationType.INSERT)@Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)" +" VALUES" +" (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")void insert(Category category);

@Before("autoFillPointCut()")

@Before("autoFillPointCut()") 中的参数 "autoFillPointCut()" 用来指定前置通知的切入点。它告诉 AOP 框架,这个前置通知(autoFill() 方法)应该在什么地方执行。 

"autoFillPointCut()" 是一个切入点表达式,引用了之前用 @Pointcut 注解定义的切入点方法 autoFillPointCut()

@Pointcut 定义的 autoFillPointCut() 方法:

@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}

 这个方法并没有实际的代码执行,它仅仅是一个标识符,用来描述一个切入点,指定了哪些方法应该被拦截。它定义了拦截 com.sky.mapper 包下所有带有 @AutoFill 注解的方法。

然后,@Before("autoFillPointCut()") 表示:

在所有符合 autoFillPointCut() 所定义的切入点表达式的目标方法之前,执行 autoFill() 方法。

JoinPoint

JoinPoint 是 AOP(面向切面编程)中的一个核心概念,它代表了在程序执行过程中的某个连接点。在 Spring AOP 中,JoinPoint 通常指的是拦截的方法调用,你可以通过 JoinPoint 获取很多与当前执行方法有关的信息,比如方法名、参数、目标对象等。

在 Spring AOP 中,JoinPoint 表示一个拦截点,即某个被拦截的方法执行时的上下文信息。你可以把 JoinPoint 看作一个对象,里面存储了和当前拦截方法相关的各种数据。通过 JoinPoint,我们可以访问到很多和当前方法执行有关的详细信息。

你能通过 JoinPoint 获取哪些信息?

  • 目标方法的签名(方法名称、返回类型、参数类型等)。
  • 目标方法的参数
  • 目标对象(即该方法所属的对象)。
  • 方法执行的位置(前置通知、后置通知等)。

例子中的 JoinPoint

在代码中,JoinPoint 被传递到了 autoFill() 方法里。这个 JoinPoint 表示当前拦截的数据库操作方法(如插入或更新操作)。我们通过 JoinPoint 来获取方法的签名、注解、以及被调用的方法的参数。

获取方法签名和注解

MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获取方法上的注解对象
OperationType operationType = autoFill.value(); // 获得数据库操作类型

joinPoint.getSignature():获取当前被拦截方法的签名信息。Signature 是方法的签名对象,包含方法名、返回类型、参数类型等信息。

MethodSignature:是 Signature 的子类,提供了更多关于方法的信息。这里通过类型转换,将 Signature 转为 MethodSignature

signature.getMethod().getAnnotation(AutoFill.class):获取方法上的 @AutoFill 注解对象。通过这个注解对象可以获取到注解的元数据,比如 value(数据库操作类型)。

autoFill.value():获取 @AutoFill 注解中的 value 属性,它表示数据库操作的类型(如 INSERT 或 UPDATE)。

获取被拦截方法的参数

Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {return;
}
Object entity = args[0];

joinPoint.getArgs():获取当前被拦截方法的参数列表(数组形式)。如果参数为空或参数长度为 0,说明没有实体对象需要填充,直接返回。

Object entity = args[0];:假设被拦截的方法的第一个参数是需要填充的实体对象,将其取出用于后续操作。

反射

什么是反射

反射是 Java 提供的一种功能,允许在运行时动态获取类的信息,并且可以操作这些类的信息,比如获取类的字段、方法、构造函数,甚至调用方法。它的关键在于灵活性,因为我们可以在编译时不知道类的细节,但在运行时操作它们。

通常情况下,我们编写代码时,类、方法、属性等信息都是在编译时就已经确定好的。但是在某些情况下,我们需要编写更加通用的代码,让代码在不知道具体类型的情况下,仍然能够操作这些对象。这种需求就可以通过反射实现。

获取 Class 对象

反射的核心在于获取类的**Class 对象**,通过这个对象可以获取类的各种信息。你有三种常用方式获取 Class 对象:

1.通过类名

Class<?> clazz = Class.forName("com.example.MyClass");

Class.forName() 通过类的全限定名(包名+类名)来获取类的 Class 对象。这种方式适用于你知道类名(可能从配置文件或数据库中读取)的情况。 

2.通过类的实例: 

MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();

通过一个对象实例来获取该对象的 Class 对象。这种方式适用于你已经有该类的实例对象的情况。 

3.通过类的字面量: (本次公共字段技术就是通过类的字面量)

Class<?> clazz = MyClass.class;

直接通过类名加 .class 获取 Class 对象。这种方式适用于在代码中直接指定类的情况。 

获取 Method 对象

一旦你有了 Class 对象,你可以通过**getDeclaredMethod()** 方法来获取类中的某个方法。getDeclaredMethod() 需要两个参数:

        第一个参数:方法名(字符串形式)。

        第二个参数:方法的参数类型(可以是多个,如果方法有多个参数)。

Method method = clazz.getDeclaredMethod("setCreateTime", LocalDateTime.class);

这个代码的作用是通过类的 Class 对象 clazz 获取名为 setCreateTime 的方法,该方法接受一个 LocalDateTime 类型的参数。

方法名和参数类型必须完全匹配,否则会抛出 NoSuchMethodException。所以在使用时,你需要确保方法名称和参数类型一致。

动态调用方法——invoke()

Method 对象不仅仅能用来描述类中的某个方法,它还提供了一个功能强大的方法——invoke(),用来在运行时调用方法。

invoke() 方法需要两个参数:

  1. 调用哪个对象上的方法:也就是你想在哪个对象上执行该方法。
  2. 传递给方法的参数值:传递给方法的实际参数值(如果方法有多个参数,这些参数依次传入)。

假设有如下代码:

public class User {private LocalDateTime createTime;private Long createUser;public void setCreateTime(LocalDateTime createTime) {this.createTime = createTime;}public void setCreateUser(Long createUser) {this.createUser = createUser;}
}

 我们希望通过反射调用 setCreateTimesetCreateUser 方法:

// 获取 User 类的 Class 对象
Class<?> clazz = user.getClass();// 获取 setCreateTime 方法对象
Method setCreateTime = clazz.getDeclaredMethod("setCreateTime", LocalDateTime.class);// 获取 setCreateUser 方法对象
Method setCreateUser = clazz.getDeclaredMethod("setCreateUser", Long.class);// 创建要传入的方法参数
LocalDateTime now = LocalDateTime.now();
Long currentUserId = 12345L;// 通过反射调用 setCreateTime 方法,给 createTime 字段赋值
setCreateTime.invoke(user, now);// 通过反射调用 setCreateUser 方法,给 createUser 字段赋值
setCreateUser.invoke(user, currentUserId);

在这里,我们通过反射动态调用了 user 对象的 setCreateTimesetCreateUser 方法,分别给 createTimecreateUser 字段赋值。

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

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

相关文章

VMware Fusion Pro 13 Mac版虚拟机 安装Win11系统教程

Mac分享吧 文章目录 Win11安装完成&#xff0c;软件打开效果一、VMware安装Windows11虚拟机1️⃣&#xff1a;准备镜像2️⃣&#xff1a;创建虚拟机3️⃣&#xff1a;虚拟机设置4️⃣&#xff1a;安装虚拟机5️⃣&#xff1a;解决连不上网问题 安装完成&#xff01;&#xff0…

港科夜闻 | 叶玉如校长出席2024科技+新质生产力高峰论坛发表专题演讲,贡献国家科技强国战略...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、叶玉如校长出席“2024科技新质生产力高峰论坛”&#xff0c;做了题为“三个创新&#xff1a;培育和发展新质生产力、贡献国家科技强国战略”的主题演讲。该论坛于9月2日在香港召开。论坛围绕夯实基础科研、推动源头创新、…

打造安心宠物乐园:EasyCVR平台赋能猫咖/宠物店的智能视频监控解决方案

随着宠物经济的蓬勃发展&#xff0c;宠物店与猫咖等场所对顾客体验、宠物安全及健康管理的需求日益提升。然而&#xff0c;如何确保这些场所的安全与秩序&#xff0c;同时提升顾客体验&#xff0c;成为了经营者们关注的焦点。引入高效、智能的视频监控方案&#xff0c;不仅能够…

微信企业微信忽然爆满 怎么清理才干净?一招彻底清理干净垃圾文件

大家都知道&#xff0c;微信用久了就会堆积很多的垃圾&#xff0c;然后导致系统空间不足&#xff0c;继而引发一些不必要的系统问题&#xff0c;所以清理掉这些垃圾是有必要的。今天我们就给大家介绍一个可以清理微信和企业微信的工具。 解决微信&企业微信忽然爆满的步骤&…

PMP–一、二、三模–分类–14.敏捷–技巧–变革就绪情况

文章目录 技巧一模14.敏捷–组织考虑因素--变革就绪情况--管理层的变更意愿&#xff0c;是组织是否变革就绪的重要前提。答案关键字&#xff0c;获得支持107、 [单选] 一家沉浸于传统瀑布式项目管理中的PMO聘请了你&#xff0c;作为敏捷实践者&#xff0c;来指导组织向敏捷的转…

项目进度一

一.双token验证登陆/注册 1.在前端中&#xff0c;得到响应记录acessToken和 refreshToken ,并记录在 localStorage中&#xff0c;当登录之后的请求都要携带着accessToken,如果accessToken过期&#xff0c;就再验证一下refreshToken&#xff0c;如果也过期就需要重新登录&#…

标准库标头 <optional> (C++17)学习之optional

类模板 std::optional 管理一个可选 &#xfeff;的所含值&#xff0c;即既可以存在也可以不存在的值。 一种常见的 optional 使用情况是作为可能失败的函数的返回值。与如 std::pair<T, bool> 等其他手段相比&#xff0c;optional 可以很好地处理构造开销高昂的对象&a…

数据库的操作:SQL语言的介绍

一.前言 SQL是一种结构化查询语言。关系型数据库中进行操作的标准语言。 二.特点 ①对大小写不敏感 例如&#xff1a;select与Select是一样的 ②结尾要使用分号 没有分号认为还没结束; 三.分类 ①DDL&#xff1a;数据定义语言&#xff08;数据库对象的操作&#xff08;结…

通义千问更新数学大模型及视觉多模态

Qwen2-Math&#xff0c;这是通义千问专门为数学场景优化的模型&#xff0c;其数学能力指标甚至超越了GPT4o, Claude3.5 Sonnet, Deepseek Coder等顶流模型&#xff0c;目前从指标来看是最强的数学模型。目前是免费供应&#xff0c;大家碰到数学问题可以选择使用这个模型。 Qw…

开放式耳机对耳朵伤害大吗?超舒适开放式耳机推荐!

开放式耳机通常被认为对耳朵的伤害相对较小。这种耳机的设计不深入耳道&#xff0c;允许空气流通&#xff0c;减少了耳道内潮湿和细菌滋生的风险&#xff0c;同时也降低了因耳道封闭造成的不适和炎症可能性。开放式耳机的佩戴方式通常更为舒适&#xff0c;减少了对耳道的摩擦和…

数字化转型的战略规划应该怎么做?(附IBM-IT战略规划方法论PPT下载)

IBM-IT战略规划方法论PPT-下载链接见文末~ 数字化转型的战略规划是一个系统而复杂的过程&#xff0c;需要从多个维度进行考虑和规划。以下是一些关键步骤和建议&#xff0c;以帮助企业制定有效的数字化转型战略规划&#xff1a; 1. 明确数字化转型愿景和目标 设定愿景&#…

‌AI大模型微调技术:‌AI全栈大模型项目实战,人工智能视,多模态大模型

‌AI大模型微调技术&#xff1a;‌解锁迁移学习的潜力‌ 在人工智能领域&#xff0c;‌大模型微调技术正逐渐崭露头角&#xff0c;‌成为迁移学习中的一项重要技术。‌本文将深入探讨AI大模型微调技术的原理、‌方法以及其在实际应用中的潜力。‌ 一、‌微调技术的概念与原理…

解决el-table中使用el-input无法聚焦问题

在el-table中点击单元格时使用el-input或其他表单组件编辑单条数据。会出现聚焦不上的问题&#xff0c;需要手动点击才能够聚焦。究其原因是因为点击单元格时页面已自动聚焦到单元格&#xff0c;此时无法自动聚焦到对应的表单&#xff0c;需要手动设置。 <template><e…

ICM20948 DMP代码详解(9)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;8&#xff09; 上一回解析完了EMP-App中的入口函数main()中重点关注的第2段代码&#xff0c;本回继续往下解析。为了便于理解和回顾&#xff0c;在此贴出main函数代码&#xff0c;如下&#xff1a; int main (void) …

UE5 Linux编译流程(实战)

文章目录 概述setup.sh2.GenerateProjectFiles.sh3.make其他的 小结 概述 之前写过一篇linux上代码的流程&#xff0c;这一篇&#xff0c;补下编译流程。4.26还是4.27的时候&#xff0c;做过编译&#xff0c;那会刚出来&#xff0c;当时编译用的是QT&#xff0c;跟着文档&…

2024年印尼金融科技报告解读(1) | 印尼金融科技发展现状与挑战

概述 金融科技的创新正加速全球金融普及和经济增长&#xff0c;尤其在东南亚&#xff0c;其影响尤为显著。 印尼作为该区域的经济龙头&#xff0c;凭借其庞大的人口基数和独特的地理位置&#xff0c;对"非接触式"服务和中小企业融资的需求迅猛增长&#xff0c;成为数…

12.1.1.案例专题-数据流图

大纲 数据流图 DFD 基本图形元素 外部实体、加工、数据存储、数据流 灰洞、黑洞、奇迹 考题中如果出现黑洞&#xff0c;说明至少缺一条输出 考题中如果出现奇迹&#xff0c;说明至少缺一条输入 考题中几乎不会考到 灰洞 顶层图、0层图、1层图 考试中不会出现 1层图&#x…

2024最新!Facebook手机版和网页版改名教程!

Facebook作为全球最大的社交平台之一&#xff0c;允许用户自定义名字和昵称。在Facebook更新姓名可以帮助您更好的展现账号形象。本文将为您提供详细的步骤指导&#xff0c;帮助您在手机APP和网页版上轻松完成Facebook改名操作。 Facebook手机版改名 打开Facebook APP并登录账号…

Transformer、RNN和SSM的相似性探究:揭示看似不相关的LLM架构之间的联系

通过探索看似不相关的大语言模型(LLM)架构之间的潜在联系,我们可能为促进不同模型间的思想交流和提高整体效率开辟新的途径。 尽管Mamba等线性循环神经网络(RNN)和状态空间模型(SSM)近来备受关注,Transformer架构仍然是LLM的主要支柱。这种格局可能即将发生变化:像Jamba、Samb…

[数据集][目标检测]肺炎检测数据集VOC+YOLO格式4983张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4983 标注数量(xml文件个数)&#xff1a;4983 标注数量(txt文件个数)&#xff1a;4983 标注…