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

Spring AOP详细解析

AOP

AOP:面向切面编程,将与业务无关,却为业务所共同调用的逻辑封装起来,减少系统的重复度,降低模块间的耦合度。对于一些“弱共性”。AOP可以同一对他们进行抽象和集中处理

AspectJ:切面,@ Aspect

JoinPoint:连接点,程序执行过程中的可被切面拦截的特定点

Advice:通知,定义横切逻辑

  1. @Before: 在方法之前执行增强
  2. @ After在方法之后执行通知
  3. @ Around:在方法执行前后都通知
  4. @ AfterReturning:在方法执行后返回结果执行通知
  5. @ AfterThrowing: 在方法抛出异常后执行通知

PointCut:切点,匹配连接点

@Pointcut("execution(public * com.neilxu..*Controller.*(..))")

AOP实现机制-基于动态代理

动态代理:运行时动态生成代理对象,允许开发者运行时指定需要代理的接口和行为,在不修改原始类的情况下对方法调用进行增强和拦截。

基于JDK的动态代理:基于reflect包的Proxy和InvolationHandler接口实现,需要代理的类实现一个或者多个接口

基于CGLIB的动态代理:当代理的类没有实现接口时,使用CGLIB生成被代理类的子类作为代理

能使用静态代理的方式实现AOP吗?

AOP首先在切面(Aspect)中定义一个Advice(增强),将advice织入到对象的方法中

可以,但是有较多缺点:

  1. 代码爆炸:假如有100个Service类需要加事务,就需要写100个静态代理类
  2. 僵化:一旦业务改名,所以相关的代理类都得改名
  3. 无法动态筛选:假如只想给某个注解得方法加事务,静态代理必须写死逻辑
package com.neilxu.train.member.aspect;import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;@Aspect
@Component
public class LogAspect {public LogAspect() {System.out.println("LogAspect");}private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);/*** 定义一个切点*///任意返回类型,neilxu下任意包(项目名/模块名),任意Controller类下任意方法任意参数@Pointcut("execution(public * com.neilxu..*Controller.*(..))")public void controllerPointcut() {}@Before("controllerPointcut()")public void doBefore(JoinPoint joinPoint) {// 增加日志流水号MDC.put("LOG_ID", System.currentTimeMillis() + RandomUtil.randomString(3));// 开始打印请求日志ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();Signature signature = joinPoint.getSignature();String name = signature.getName();// 打印请求信息LOG.info("------------- 开始 -------------");LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);LOG.info("远程地址: {}", request.getRemoteAddr());// 打印请求参数Object[] args = joinPoint.getArgs();// LOG.info("请求参数: {}", JSONObject.toJSONString(args));// 排除特殊类型的参数,如文件类型Object[] arguments = new Object[args.length];for (int i = 0; i < args.length; i++) {if (args[i] instanceof ServletRequest|| args[i] instanceof ServletResponse|| args[i] instanceof MultipartFile) {continue;}arguments[i] = args[i];}// 排除字段,敏感字段或太长的字段不显示:身份证、手机号、邮箱、密码等String[] excludeProperties = {"mobile"};PropertyPreFilters filters = new PropertyPreFilters();PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();excludefilter.addExcludes(excludeProperties);LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));}@Around("controllerPointcut()")public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = proceedingJoinPoint.proceed();// 排除字段,敏感字段或太长的字段不显示:身份证、手机号、邮箱、密码等String[] excludeProperties = {"mobile"};PropertyPreFilters filters = new PropertyPreFilters();PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();excludefilter.addExcludes(excludeProperties);LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);return result;}}

http://www.dtcms.com/a/305517.html

相关文章:

  • 基于deepseek的事件穿透分析-风险传导图谱
  • 基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(六)
  • Tomcat线程池、业务线程池与数据库连接池的层级约束关系解析及配置优化
  • 在Trae中使用MoonBit月兔
  • 《Computational principles and challenges in single-cell data integration》
  • Map 集合
  • vue 使用postcss-pxtorem 实现适老化
  • Elasticsearch 基础速成 5 步跑通索引、文档、映射与查询
  • 【php 安装 xdebug】
  • 数学建模——最大最小化模型
  • 关于mysql时间类型和java model的日期类型映射
  • anaconda和Miniconda安装包32位64位皆可,anaconda和Miniconda有什么区别?
  • 【33】C# WinForm入门到精通 ——表格布局器TableLayoutPanel【属性、方法、事件、实例、源码】
  • JetBrains Annotations:从入门到落地,彻底告别 NullPointerException
  • Vue路由钩子完全指南
  • Linux ARM 平台 C 语言操作 Excel 文件的常用库与工具汇总(支持 xls 和 xlsx)
  • 【 建模分析回顾】[MultiOutputClassifier]MAP - Charting Student Math Misunderstandings
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-51,(知识点:stm32,GPIO基础知识)
  • Java stream 并发问题
  • 2025年6月电子学会青少年软件编程(C语言)等级考试试卷(二级)
  • 潇洒郎: Kafka Ubuntu 安装部署,命令行或者python生产数据与消费数据(kafka-python)
  • makefile中include *.d文件的作用
  • 安全和AI方向的学习路线
  • aws(学习笔记第五十课) ECS集中练习(2)
  • 项目目标如何拆解,才能提高执行效率和效果
  • 获取TensorRT引擎文件(.engine)版本号的几种方法
  • GitPython02-Git使用方式
  • 【Datawhale AI夏令营】科大讯飞AI大赛(大模型技术)/夏令营:让AI理解列车排期表(Task3)
  • Elasticsearch 全文检索与过滤
  • MyBatis Plus Wrapper 详细分析与原理