面向切面编程(AOP)@Aspect‌

发布时间:2026/5/27 21:37:28

面向切面编程(AOP)@Aspect‌ ‌Aspect‌ 是 Spring 框架以及 AspectJ中用于‌面向切面编程AOP‌的一个核心注解。它的作用是将一个普通的 Java 类标记为‌切面Aspect‌。切面是一个独立的模块专门用来封装那些散布在应用各处、与核心业务逻辑无关的通用功能即“横切关注点”例如日志记录事务管理安全检查与权限认证性能监控缓存处理通过Aspect注解开发者可以在不修改原有业务代码的情况下把这些通用逻辑“织入”到目标方法执行的前、后或异常时从而实现关注点分离让代码更清晰、更易维护。在切面类内部通常还会配合使用其他注解来指定“何时”以及“在哪些方法上”执行切面逻辑‌Pointcut‌定义切入点即拦截哪些类或方法。‌Before / After / AfterReturning / AfterThrowing / Around‌定义通知Advice即在切入点上执行的增强逻辑。示例Aspect Component public class AutoFillAspect { // 切入点拦截所有带AutoFill注解的方法 Pointcut(annotation(autoFill)) public void pointCut(AutoFill autoFill) {} // 前置通知在方法执行前填充字段 Before(pointCut(autoFill)) public void before(JoinPoint joinPoint, AutoFill autoFill) { // 获取当前登录用户 String username SecurityUtils.getUsername(); if (username null || username.isEmpty()) { username String.valueOf(SecurityUtils.getUserId()); // 默认值 } Date now new Date(); // 获取方法参数并处理 Object[] args joinPoint.getArgs(); if (args ! null args.length 0) { for (Object arg : args) { // 递归处理所有参数包括集合和嵌套对象 processObject(arg, autoFill.value(), username, now); } } } /** * 递归处理对象支持单对象、集合、数组和嵌套对象 */ private void processObject(Object obj, AutoFill.OperationType operationType, String username, Date now) { if (obj null) { return; } // 如果是集合处理集合中的每个元素 if (obj instanceof Collection?) { Collection? collection (Collection?) obj; for (Object item : collection) { processObject(item, operationType, username, now); } return; } // 如果是数组处理数组中的每个元素 if (obj.getClass().isArray()) { Object[] array (Object[]) obj; for (Object item : array) { processObject(item, operationType, username, now); } return; } // 处理继承BaseEntity的对象 if (obj instanceof BaseEntity) { BaseEntity entity (BaseEntity) obj; fillBaseEntity(entity, operationType, username, now); } // 处理对象中的嵌套实体递归检查所有字段 try { Field[] fields obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object fieldValue field.get(obj); // 如果字段是集合或实体类递归处理 if (fieldValue ! null) { // 集合类型 if (fieldValue instanceof Collection? || fieldValue.getClass().isArray()) { processObject(fieldValue, operationType, username, now); } // 自定义对象类型非基本类型 else if (!field.getType().isPrimitive() !field.getType().getName().startsWith(java.) !field.getType().isEnum()) { processObject(fieldValue, operationType, username, now); } } } } catch (Exception e) { // 反射处理异常不影响主流程 e.printStackTrace(); } } /** * 填充BaseEntity的字段 */ private void fillBaseEntity(BaseEntity entity, AutoFill.OperationType operationType, String username, Date now) { // 插入操作填充创建和更新字段 if (operationType AutoFill.OperationType.INSERT || operationType AutoFill.OperationType.ALL) { // 只有为空时才填充允许手动设置值 if (entity.getCreateBy() null) { entity.setCreateBy(username); } if (entity.getCreateTime() null) { entity.setCreateTime(now); } if (entity.getUpdateBy() null) { entity.setUpdateBy(username); } if (entity.getUpdateTime() null) { entity.setUpdateTime(now); } } // 更新操作只填充更新字段 if (operationType AutoFill.OperationType.UPDATE || operationType AutoFill.OperationType.ALL) { if (entity.getUpdateBy() null) { entity.setUpdateBy(username); } if (entity.getUpdateTime() null) { entity.setUpdateTime(now); } } } }1.Pointcut切入点定义Pointcut(annotation(autoFill))表示拦截所有‌标注了AutoFill注解‌的方法。这里使用了annotation()表达式不仅能拦截到目标方法还‌直接将注解对象绑定到参数autoFill中‌供后续的通知方法使用。注解AutoFill通常携带一个枚举值如INSERT或UPDATE用于区分当前是新增操作还是修改操作。2.Before前置通知‌Before‌前置通知保证这段逻辑在目标方法‌执行之前‌运行。‌JoinPoint joinPoint‌AspectJ 框架提供的连接点对象包含目标方法的所有信息通过joinPoint.getSignature()获取方法签名进而拿到方法名、方法上的注解等。通过joinPoint.getArgs()获取目标方法的实参列表通常第一个参数就是要操作的实体对象。‌AutoFill autoFill‌切入点绑定的注解对象通过autoFill.value()即可拿到当前操作类型INSERT 还是 UPDATE。before方法内部大致会做以下事情‌获取操作类型‌从autoFill.value()得知是 INSERT 还是 UPDATE。‌获取实体对象‌通过joinPoint.getArgs()拿到方法传入的实体对象约定实体放在第一个参数位置。‌准备填充数据‌获取当前时间LocalDateTime.now()并从上下文如BaseContext或ThreadLocal获取当前登录用户的 ID。‌反射赋值‌通过反射调用实体类的 setter 方法进行字段填充如果是 ‌INSERT‌新增填充createTime、updateTime、createUser、updateUser四个字段。如果是 ‌UPDATE‌修改仅填充updateTime、updateUser两个字段。

相关新闻