news 2026/4/23 21:00:41

Spring AOP连接点实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AOP连接点实战解析

🎯AOP连接点(JoinPoint)详解

连接点是AOP中程序执行过程中的一个特定点,它是切面可以插入增强逻辑的位置。


📍连接点是什么?

生动的比喻

程序执行就像一部电影 🎬 连接点 → 电影的关键帧(可以插入特效的地方) 切面 → 特效团队 增强 → 实际添加的特效(慢动作、滤镜等)

更技术化的理解

连接点是程序运行中可以被拦截的点,例如:

  • 方法调用
  • 方法执行
  • 异常抛出
  • 字段访问
  • 对象初始化

在Spring AOP中,连接点特指:方法的执行


🎪连接点的具体形式

1. 方法执行(Method Execution)Spring AOP唯一支持的

publicclassUserService{publicvoidsaveUser(Useruser){// ← 这是一个连接点// 方法体}}

2. 方法调用(Method Call)Spring AOP不支持

publicclassController{publicvoidprocess(){userService.saveUser(user);// ← 这是一个调用连接点}}

3. 其他连接点(AspectJ支持,但Spring AOP不支持)

// 构造器执行newUserService();// ← 连接点// 字段访问user.name;// ← 连接点// 异常处理thrownewException();// ← 连接点

🔧JoinPoint对象详解

在通知方法中,可以通过JoinPoint参数获取连接点信息:

JoinPoint核心方法

publicinterfaceJoinPoint{// 获取方法签名SignaturegetSignature();// 获取目标对象ObjectgetTarget();// 获取代理对象ObjectgetThis();// 获取方法参数Object[]getArgs();// 获取连接点类型(Spring AOP中总是:method-execution)StringgetKind();// 获取静态部分信息JoinPoint.StaticPartgetStaticPart();// 获取源位置信息(文件名、行号等)SourceLocationgetSourceLocation();}

Signature对象

publicinterfaceSignature{StringgetName();// 方法名:saveUserintgetModifiers();// 修饰符:publicClassgetDeclaringType();// 声明类:UserServiceStringgetDeclaringTypeName();// 类名:com.example.UserService// 更多信息StringtoShortString();// 简短字符串StringtoLongString();// 完整字符串}

💻实际使用示例

示例1:获取方法信息

@Before("execution(* com.example.service.*.*(..))")publicvoidbeforeAdvice(JoinPointjoinPoint){// 1. 获取方法签名MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();System.out.println("=== 连接点信息 ===");System.out.println("方法名: "+signature.getName());// saveUserSystem.out.println("方法全名: "+signature.toLongString());// public void com.example.UserService.saveUser(User)System.out.println("声明类: "+signature.getDeclaringTypeName());// com.example.UserServiceSystem.out.println("返回类型: "+signature.getReturnType());// void// 2. 获取参数Object[]args=joinPoint.getArgs();for(inti=0;i<args.length;i++){System.out.println("参数"+i+": "+args[i]+" (类型: "+signature.getParameterTypes()[i]+")");}// 3. 目标对象和代理对象System.out.println("目标对象: "+joinPoint.getTarget().getClass());// UserService$$EnhancerBySpringCGLIBSystem.out.println("代理对象: "+joinPoint.getThis().getClass());// UserService$$EnhancerBySpringCGLIB}

示例2:环绕通知中使用ProceedingJoinPoint

@Around("execution(* com.example.service.*.*(..))")publicObjectaroundAdvice(ProceedingJoinPointjoinPoint)throwsThrowable{// ProceedingJoinPoint是JoinPoint的子接口,多了proceed()方法System.out.println("【环绕通知开始】方法: "+joinPoint.getSignature().getName());// 1. 可以修改参数Object[]args=joinPoint.getArgs();if(args.length>0&&args[0]instanceofString){args[0]=((String)args[0]).toUpperCase();// 修改参数}// 2. 执行目标方法longstart=System.currentTimeMillis();Objectresult=joinPoint.proceed(args);// 可以传入修改后的参数longend=System.currentTimeMillis();System.out.println("【环绕通知结束】耗时: "+(end-start)+"ms");// 3. 可以修改返回值if(resultinstanceofString){result="处理后的结果: "+result;}returnresult;}

📊连接点 vs 切入点 vs 通知

概念定义比喻
连接点程序执行中的具体点(如方法执行)电影中的具体帧
切入点匹配连接点的表达式(筛选哪些连接点)选中的帧的范围(如所有打斗场景)
通知在连接点执行的增强逻辑在选中的帧上添加的特效

三者关系

@Aspect@ComponentpublicclassLogAspect{// 切入点:匹配哪些连接点@Pointcut("execution(* com.example.service.*.*(..))")publicvoidserviceMethods(){}// ← 切入点表达式// 通知:在连接点上执行的逻辑@Before("serviceMethods()")// ← 应用到切入点匹配的连接点publicvoidlogBefore(JoinPointjoinPoint){// ← JoinPoint代表具体的连接点// 这里是通知逻辑System.out.println("执行方法: "+joinPoint.getSignature().getName());}}

🎯获取连接点信息的实用工具类

@ComponentpublicclassJoinPointUtils{/** * 获取方法参数Map */publicstaticMap<String,Object>getArgsMap(JoinPointjoinPoint){Map<String,Object>argsMap=newHashMap<>();if(joinPoint.getSignature()instanceofMethodSignature){MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();String[]parameterNames=signature.getParameterNames();Object[]args=joinPoint.getArgs();for(inti=0;i<parameterNames.length;i++){argsMap.put(parameterNames[i],args[i]);}}returnargsMap;}/** * 获取方法注解 */publicstatic<TextendsAnnotation>TgetMethodAnnotation(JoinPointjoinPoint,Class<T>annotationClass){MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Methodmethod=signature.getMethod();returnmethod.getAnnotation(annotationClass);}/** * 获取类注解 */publicstatic<TextendsAnnotation>TgetClassAnnotation(JoinPointjoinPoint,Class<T>annotationClass){Class<?>targetClass=joinPoint.getTarget().getClass();returntargetClass.getAnnotation(annotationClass);}/** * 获取完整方法路径 */publicstaticStringgetFullMethodName(JoinPointjoinPoint){StringclassName=joinPoint.getSignature().getDeclaringTypeName();StringmethodName=joinPoint.getSignature().getName();returnclassName+"."+methodName;}/** * 获取IP地址(Web环境下) */publicstaticStringgetIpAddress(JoinPointjoinPoint){try{ServletRequestAttributesattributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();if(attributes!=null){HttpServletRequestrequest=attributes.getRequest();returnrequest.getRemoteAddr();}}catch(Exceptione){// 忽略}return"unknown";}}

使用示例

@Before("execution(* com.example.service.*.*(..))")publicvoidbeforeAdvice(JoinPointjoinPoint){// 使用工具类Map<String,Object>args=JoinPointUtils.getArgsMap(joinPoint);StringfullMethodName=JoinPointUtils.getFullMethodName(joinPoint);System.out.println("方法: "+fullMethodName);System.out.println("参数: "+args);// 获取特定注解LoglogAnnotation=JoinPointUtils.getMethodAnnotation(joinPoint,Log.class);if(logAnnotation!=null){System.out.println("日志级别: "+logAnnotation.level());}}

🚀实战场景

场景1:操作日志记录

@Aspect@ComponentpublicclassOperationLogAspect{@AfterReturning(pointcut="@annotation(log)",returning="result")publicvoidlogOperation(JoinPointjoinPoint,@annotation(log)OperationLoglog,Objectresult){// 从连接点获取信息StringmethodName=joinPoint.getSignature().getName();StringclassName=joinPoint.getSignature().getDeclaringTypeName();Object[]args=joinPoint.getArgs();// 构建日志LogRecordrecord=LogRecord.builder().module(log.module()).operation(log.operation()).method(className+"."+methodName).params(JSON.toJSONString(args)).result(JSON.toJSONString(result)).ip(JoinPointUtils.getIpAddress(joinPoint)).build();// 保存日志logService.save(record);}}

场景2:参数验证

@Aspect@ComponentpublicclassValidationAspect{@Before("execution(* com.example.controller.*.*(..))")publicvoidvalidateParams(JoinPointjoinPoint){// 获取所有参数Object[]args=joinPoint.getArgs();for(Objectarg:args){if(arginstanceofBaseDTO){// 执行DTO验证ValidationResultresult=validator.validate(arg);if(!result.isValid()){thrownewValidationException(result.getErrors());}}}}}

场景3:缓存切面

@Aspect@ComponentpublicclassCacheAspect{@Around("@annotation(cacheable)")publicObjectcacheResult(ProceedingJoinPointjoinPoint,Cacheablecacheable)throwsThrowable{// 生成缓存key:类名+方法名+参数Stringkey=generateCacheKey(joinPoint);// 先从缓存获取Objectcached=cache.get(key);if(cached!=null){returncached;}// 执行方法Objectresult=joinPoint.proceed();// 存入缓存cache.put(key,result,cacheable.ttl(),TimeUnit.SECONDS);returnresult;}privateStringgenerateCacheKey(ProceedingJoinPointjoinPoint){StringBuilderkey=newStringBuilder();// 类名key.append(joinPoint.getSignature().getDeclaringTypeName()).append(".");// 方法名key.append(joinPoint.getSignature().getName()).append(":");// 参数(简单处理)for(Objectarg:joinPoint.getArgs()){key.append(arg!=null?arg.toString():"null").append(",");}returnkey.toString();}}

⚠️重要限制

Spring AOP只支持方法执行连接点

// ✅ 支持的@Before("execution(* *.*(..))")// ❌ 不支持的(需要AspectJ)@Before("call(* *.*(..))")// 方法调用@Before("initialization(*.new(..))")// 构造器@Before("get(* *)")// 字段读取@Before("set(* *)")// 字段设置

代理机制的影响

  • JDK动态代理:只能代理接口方法,thistarget可能不同
  • CGLIB代理:可以代理类,thistarget通常相同
  • 内部方法调用不会被拦截(因为不走代理)

🧪调试技巧

打印连接点信息

@Before("execution(* *.*(..))")publicvoiddebugJoinPoint(JoinPointjoinPoint){System.out.println("====== JoinPoint信息 ======");System.out.println("Kind: "+joinPoint.getKind());System.out.println("Signature: "+joinPoint.getSignature());System.out.println("SourceLocation: "+joinPoint.getSourceLocation());System.out.println("StaticPart: "+joinPoint.getStaticPart());System.out.println("==========================");}

判断连接点类型

@Before("execution(* *.*(..))")publicvoidcheckJoinPoint(JoinPointjoinPoint){if(joinPointinstanceofProceedingJoinPoint){System.out.println("这是ProceedingJoinPoint(环绕通知可用)");}else{System.out.println("这是普通JoinPoint");}}

💎总结

连接点的核心要点

  1. 定义:程序执行中可以插入增强的点
  2. Spring AOP:只支持方法执行这一种连接点
  3. JoinPoint对象:包含方法签名、参数、目标对象等信息
  4. ProceedingJoinPoint:用于环绕通知,可以控制方法执行

一句话记忆

连接点就像程序的"穴位",切入点就是"针灸的位置图",通知就是"扎针的治疗手法"。

实用口诀

连接点,执行点,方法执行最常见 JoinPoint,信息全,签名参数都在里边 ProceedingJoinPoint更强,可以控制方法执行权 记住Spring有局限,只支持方法执行这一个连接点
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 9:19:19

高效处理数据的ProcessX方法

ProcessX数据处理方法ProcessX数据处理涉及多种技术和工具&#xff0c;适用于不同场景的数据清洗、转换和分析需求。以下是几种常见的处理方法&#xff1a;数据清洗与预处理 使用Python的Pandas库可以高效处理缺失值、重复数据和异常值。例如&#xff0c;df.dropna()删除缺失值…

作者头像 李华
网站建设 2026/4/23 9:17:42

38、Python编程:回调函数、数据处理与系统管理全解析

Python编程:回调函数、数据处理与系统管理全解析 1. 回调函数基础 回调函数和传递函数的概念可能对许多人来说比较陌生,但深入了解它是很有价值的。在Python中,函数是“一等公民”,这意味着可以像操作对象一样传递和处理函数。 1.1 函数作为一等公民 以下代码展示了函数…

作者头像 李华
网站建设 2026/4/23 12:10:17

FIO工具Windows终极指南:一键下载安装完整教程

FIO工具Windows终极指南&#xff1a;一键下载安装完整教程 【免费下载链接】FIO工具最新版Windows版本下载 FIO工具最新版Windows版本现已发布&#xff0c;专为Windows平台优化&#xff0c;提供高效、稳定的性能体验。本资源包含最新版FIO工具的安装包&#xff0c;支持64位系统…

作者头像 李华
网站建设 2026/4/23 10:44:48

5大关键技术解析:构建金融级容灾系统的完整指南

在数字化金融时代&#xff0c;金融容灾和云计算备份技术已成为保障业务连续性的核心支撑。随着金融业务对系统可用性要求的不断提升&#xff0c;构建高效可靠的容灾体系成为每个金融机构的技术必修课。本文将深度解析金融信息系统容灾备份的关键技术实现路径。 【免费下载链接】…

作者头像 李华
网站建设 2026/4/23 12:10:43

18、树莓派户外应用与防水及寻宝功能实现

树莓派户外应用与防水及寻宝功能实现 1. 利用脚本上传温湿度数据到 Google Docs 要使用脚本将植物的湿度和温度数据上传到 Google Docs,需要先使用自己的 Google Docs 账户详细信息对脚本进行配置。具体操作步骤如下: 1. 用喜欢的文本编辑器打开 Adafruit_DHT_googledocs…

作者头像 李华
网站建设 2026/4/23 13:30:03

Fooocus-MRE:终极AI图像生成解决方案,免费开启创意无限

Fooocus-MRE&#xff1a;终极AI图像生成解决方案&#xff0c;免费开启创意无限 【免费下载链接】Fooocus-MRE Focus on prompting and generating 项目地址: https://gitcode.com/gh_mirrors/fo/Fooocus-MRE 想要体验专业级的AI图像生成却苦于复杂的配置&#xff1f;Foo…

作者头像 李华