当前位置: 首页 > news >正文

AOP_青春版_VS_Pro版

背景:

在javaweb和ssm中学习了面向切面编程的两种方式(两种切点表达式不同),在苍穹外卖中,对于设置更新时间,创建时间,更新人,创建者为避免重复编码,将Update&Insert中的方法调用从业务逻辑中抽离出来,实现代码复用;

需求分析:

1.设置更新时间——在AOP通知中修改mapper方法中的参数

2.设置修改人——在AOP中获取线程令牌ID

“自写”码(青春版):

package com.sky.aspect; import java.time.LocalDateTime; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import com.sky.context.BaseContext; import com.sky.entity.Employee; import lombok.extern.slf4j.Slf4j; /** * insert Aop */ @Component @Aspect @Slf4j public class TimeAdvice { @Around("execution(* com.sky.mapper.*Mapper.insert*(..))") public Object SetTime(ProceedingJoinPoint joinPoint) throws Throwable{ log.info("*******start AOP*******",BaseContext.getCurrentId()); Object [] arg=joinPoint.getArgs(); if(arg.length>0&&arg[0]!=null&&arg[0] instanceof Employee){ Employee employee=(Employee) arg[0]; employee.setCreateTime(LocalDateTime.now()); employee.setUpdateTime(LocalDateTime.now()); employee.setCreateUser(BaseContext.getCurrentId()); employee.setUpdateUser(BaseContext.getCurrentId()); } Object result=joinPoint.proceed(arg); return result; } }

可优化点:

1.修改方法参数方式

2.解决变量属性硬编码

3.代码复用性通配性不高

优化版(Pro版):

package com.sky.enumeration; public enum OperationType { UPDATE, // 更新操作 INSERT // 插入操作 } package com.sky.annotation; import com.sky.enumeration.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) // 运行时保留,便于AOP拦截 public @interface AutoFill { OperationType value(); // 指定操作类型 } package com.sky.aspect; import com.sky.annotation.AutoFill; import com.sky.enumeration.OperationType; 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.time.LocalDateTime; @Aspect @Component @Slf4j public class AutoFillAspect { /** * 定义切点:拦截所有带有@AutoFill注解的方法 */ @Pointcut("@annotation(com.sky.annotation.AutoFill)") public void autoFillPointCut() {} /** * 前置通知:在方法执行前自动填充字段 */ @Before("autoFillPointCut()") public void autoFill(JoinPoint joinPoint) { log.info("开始进行公共字段自动填充..."); // 1. 获取方法签名和注解 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); OperationType operationType = autoFill.value(); // 2. 获取方法参数(实体对象) Object[] args = joinPoint.getArgs(); if (args == null || args.length == 0) { return; } Object entity = args[0]; // 3. 获取当前时间和当前登录用户ID LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId(); // 假设有获取当前用户ID的工具类 // 4. 根据操作类型反射填充字段 try { if (operationType == OperationType.INSERT) { // 插入操作:填充createTime, updateTime, createUser, updateUser setFieldValue(entity, "createTime", now); setFieldValue(entity, "updateTime", now); setFieldValue(entity, "createUser", currentId); setFieldValue(entity, "updateUser", currentId); } else if (operationType == OperationType.UPDATE) { // 更新操作:只填充updateTime, updateUser setFieldValue(entity, "updateTime", now); setFieldValue(entity, "updateUser", currentId); } } catch (Exception e) { log.error("公共字段自动填充失败", e); } } /** * 反射设置字段值 */ private void setFieldValue(Object object, String fieldName, Object value) throws Exception { Class<?> clazz = object.getClass(); java.lang.reflect.Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } }

1.枚举(Enum)定义操作类型:

2.自定义注解 (Annotation) — 给代码"打标签

核心概念:
  • @interface= 我要定义一个新注解

  • @Target= 这个注解能贴在哪儿(方法?类?字段?)

  • @Retention= 这个注解保留到什么时候(源码?编译后?运行时?)

  • value()= 使用注解时必须填的参数

如果用预制字符串呢?

定义标签值这块使用自定义Constant类,规定Final属性字符串“Insert”和“Update”,在自定义标签中填写呢?

通过对比发现一些“安全隐患”:在标签中填写值时,仍要使用Operation.Insert这样的操作,涉及Insert书写错误的风险,会出现编译报错;其次,定义自定义标签赋值类型为String,会导致随意赋值,eg:@AutoFill("Taylorswift"),仍然可以通过编译,但会导致aop失效;

3.获取方法签名和注解 — AOP的"透视眼"

@Before("autoFillPointCut()") public void autoFill(JoinPoint joinPoint) { // JoinPoint = 连接点,代表被拦截的方法 // ========== 第一步:获取方法签名 ========== // MethodSignature 是方法的"身份证",包含方法的所有信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // signature.getName() → 方法名 "insert" // signature.getParameterTypes() → 参数类型 // signature.getReturnType() → 返回值 // ========== 第二步:从方法上读取注解 ========== // 拿到方法上的 @AutoFill 标签 AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 读取标签上的值(INSERT 还是 UPDATE) OperationType operationType = autoFill.value(); }

4.Java 反射 — 运行时"黑进"对象

// 正常赋值(编译时确定):employee.setCreateTime(now); // 反射赋值(运行时动态):不管对象是什么类,强行给字段赋值 private void setFieldValue(Object object, String fieldName, Object value) throws Exception { // 1. 获取对象的类模板 Class<?> clazz = object.getClass(); // 比如 Employee.class // 2. 从类模板中找到指定字段(即使它是private的) Field field = clazz.getDeclaredField(fieldName); // 找 "createTime" 字段 // 3. 暴力破解访问权限(private也能访问) field.setAccessible(true); // 4. 给这个对象的该字段赋值 field.set(object, value); // 相当于 object.fieldName = value }

优点:

1.使用自定义注解标注方法描述切入点,代码可读性高

2.通过joinpoint获取方法签名和注解与第一种方法相比代码利用率更高

3.通过条件语句判断是insert/update

4.java反射:降低耦合性,通配性更高,通过Field打通属性,修改属性值,参数修改无需新建或引用;

http://www.cnnetsun.cn/news/1532429.html

相关文章:

  • 突破直播内容保存瓶颈:DouyinLiveRecorder多平台录制全攻略
  • Retinaface+CurricularFace保姆级教学:Linux终端下人脸相似度计算全流程
  • Qwen3-TTS-VoiceDesign惊艳效果:动态砖块跳动与语音重音位置同步
  • Qwen2.5 JSON输出不稳定?结构化生成优化部署实战案例
  • Wan2.2-I2V-A14B作品集:看AI如何将普通照片变成电影级片段
  • GTE中文嵌入模型效果展示:同义句高相似、反义句低相似真实案例
  • socat-windows技术解析:跨平台网络数据转发的实战指南
  • 驱动开发的常用工具
  • RexUniNLU中文分词优化:专业领域术语识别
  • 南北阁Nanbeige 4.1-3B性能调优:针对STM32开发文档的推理加速
  • 新手入门:在快马上亲手实现第一个限流器,看懂‘rate limit exceeded’
  • Vin象棋:让AI成为你的象棋智能助手
  • 让消失的消息重现:RevokeMsgPatcher实用指南
  • WidescreenFixesPack:让80+款经典游戏在宽屏显示器上完美呈现的终极方案
  • Qwen3-TTS在自媒体中的应用:一键生成中英文口播配音教程
  • MiniSat完整指南:5分钟掌握高效SAT求解器核心技术
  • 电力考试系统
  • 【3D等变几何深度学习与分子表示】长程相互作用建模
  • 用快马快速构建countif函数交互演示原型,零代码掌握条件计数
  • 能耗监控一体化:OpenClaw+GLM-4.7-Flash分析电脑使用报告
  • 主辅助服务市场出清模型研究【旋转备用】(Matlab代码实现)
  • 你的FVC结果准吗?用Landsat 8数据时,NDVI最大值最小值千万别乱设!
  • LangFlow实战案例分享:智能问答助手工作流搭建全过程
  • 如何每天节省25分钟?淘金币任务自动化工具的终极时间管理方案
  • Pixel Dimension Fissioner 电商场景实战:海量商品描述自动生成
  • EdgeRemover技术揭秘:彻底解决Windows Edge卸载难题的智能方案
  • 语音转换技术全解析:从原理到实践的Retrieval-based Voice-Conversion-WebUI指南
  • GetQzonehistory:QQ空间记忆安全备份四步法
  • 从0到1,快速训练并使用YOLO模型
  • 代码随想录算法训练营第十天|LeetCode 232 用栈实现队列、LeetCode 225 用队列实现栈、LeetCode 20 有效的括号、LeetCode 1047 删除字符串中的所有相邻重复项