AOP_青春版_VS_Pro版
背景在javaweb和ssm中学习了面向切面编程的两种方式两种切点表达式不同在苍穹外卖中对于设置更新时间创建时间更新人创建者为避免重复编码将UpdateInsert中的方法调用从业务逻辑中抽离出来实现代码复用需求分析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 [] argjoinPoint.getArgs(); if(arg.length0arg[0]!nullarg[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 resultjoinPoint.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会导致随意赋值egAutoFill(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/update4.java反射降低耦合性通配性更高通过Field打通属性修改属性值参数修改无需新建或引用
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2457470.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!