思维导图
🌟AOP介绍
基本概念:在不改变原有功能的逻辑,增加新的功能。
应用场景:
- 权限控制
- 日志处理
- 事务控制
下面以对产品数据增删改查功能,进行日志管理功能为例,对AOP中的核心概念作出介绍,请参考下表
概念 | 解析 | 对应日志管理功能 |
核心关注点 | 业务逻辑的主要功能,应用程序主要关注的部分 | 产品数据的增删改查 |
横切关注点 | 与核心关注点相关但不属于核心关注点的功能,在系统的多个模块或组件中散布 | 记录产品操作的日志 |
通知 | 在特定切入点执行时要执行的代码,实现横切关注点的具体逻辑。可以在目标方法执行之前、之后或抛出异常时执行 | 执行记录日志的代码 |
连接点 | 可以插入通知的特定点,通常是方法执行的位置 | 产品表操作的方法 |
切入点 | 通过表达式或规则定义的连接点的集合。确定了哪些连接点与通知关联起来 | 选择所有的插入、更新或删除操作的连接点集合 |
切面 | 横切关注点和通知的组合,将通知应用到切入点匹配的连接点上 | 包含记录日志的通知和定义切入点的规则 |
目标 | 被通知的对象或类,即应用程序中执行具体操作的对象或方法 | 对产品数据作出操作的对象或方法 |
织入 | 将切面应用到目标对象上,创建新的代理对象的过程。可以在编译时、加载时或运行时进行 | 将日志管理切面应用到产品表操作的目标对象上,以记录日志 |
🌟具体实现步骤
数据准备
创建产品表、以及日志表,并向产品表添加相关数据。
CREATE TABLE `product` ( `id` int(11) NOT NULL, `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `price` decimal(10, 2) NULL DEFAULT NULL, `stock` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; -- ---------------------------- -- Records of product -- ---------------------------- INSERT INTO `product` VALUES (1, '苹果13', 10.99, 50); INSERT INTO `product` VALUES (2, '小米10', 19.99, 99); INSERT INTO `product` VALUES (3, '华为mate20', 5.99, 19); CREATE TABLE `log` ( `id` int(10) NOT NULL AUTO_INCREMENT, `operation` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `data` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `operate_time` datetime NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
引入相关依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
创建实体类
Product类
public class Product implements Serializable { private Integer id; private String name; private BigDecimal price; private Integer stock; }
Log类
public class Log implements Serializable { private Integer id; private String operation; private String data; private Timestamp operateTime; }
自定义注解@LogTip
自定义注解,用于标记记录日志的方法。
@Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogTip { String value() default ""; }
定义切面类
切面类由切点PointCut+通知Advice
共同构成。有关于通知的类型以及解释,可以查看思维导图中的通知节点
。有关于JoinPoint的API见下表
API | 描述 |
getSignature() | 获取连接点的签名,返回一个MethodSignature对象 |
getArgs() | 获取连接点方法的参数数组 |
getTarget() | 获取目标对象 |
getThis() | 获取当前代理对象 |
toShortString() | 生成连接点的简短描述 |
toLongString() | 生成连接点的详细描述 |
getStaticPart() | 获取静态连接点部分 |
getKind() | 获取连接点的类型 |
getSourceLocation() | 获取连接点所在位置的源代码位置 |
@Component @Aspect public class LogAspect { @Autowired private LogMapper logMapper; @Pointcut("@annotation(com.shoanjen.redis.annotation.LogTip)") public void pointCut(){} @AfterReturning("pointCut()") public void getLogInfo(JoinPoint point) throws Throwable { // 获取方法参数列表 Object[] args = point.getArgs(); //获取自定义注解上的描述信息 MethodSignature methodSignature= (MethodSignature) point.getSignature(); Method method=methodSignature.getMethod(); LogTip annotation=method.getAnnotation(LogTip.class); String desc=annotation.value(); // 获取操作的数据 String data = Arrays.toString(args); // 记录日志 saveLog(desc, data); } private void saveLog(String desc, String data) { Log log = new Log(); log.setOperation(desc); log.setData(data); log.setOperateTime(new Timestamp(System.currentTimeMillis())); logMapper.save(log); } }
LogMapper
public interface LogMapper { void save(Log log); }
<insert id="save" parameterType="com.shoanjen.redis.model.Log" keyColumn="id" keyProperty="id" useGeneratedKeys="true"> INSERT INTO log(operation, data, operate_time) VALUES (#{operation}, #{data}, #{operateTime}); </insert>
ProductController
这里以上一篇文章一张思维导图带你学会使用SpringBoot异步任务实现下单校验库存中的产品下单功能为例子,对其记录日志。
@RestController @RequestMapping("/api/v1/product") public class ProductController { @Autowired private ProductService productService; @Autowired private ValidateTask validateTask; @LogTip("产品下单") @RequestMapping("order") public JsonData order(@RequestParam int productId,@RequestParam int quantity) throws ExecutionException, InterruptedException { Future<Boolean> validateResult=validateTask.validateStock(productId,quantity); System.out.println(validateResult.get()); Boolean flag=false; //判断异步任务是否完成 if (validateResult.isDone()){ try { flag=validateResult.get(); } catch (Exception e) { flag=false; } } if (flag){ return JsonData.buildSuccess("下单成功"); }else { return JsonData.buildError("下单失败,库存不足"); } } }
🌟最终测试
接口请求测试
日志表查看
🌟写在最后
有关于SpringBoot使用AOP实现日志管理功能到此就结束了。感谢大家的阅读,希望大家在评论区对此部分内容散发讨论,便于学到更多的知识。