Spring AOP(一)Spring AOP 核心概念详解

张开发
2026/4/11 9:33:51 15 分钟阅读

分享文章

Spring AOP(一)Spring AOP 核心概念详解
Spring AOP一Spring AOP 核心概念详解Spring AOP 核心概念详解一、Spring AOP 七大核心概念二、用生活案例去理解AOP场景小区门禁系统三、示例代码详细解释1. **切面Aspect** - 完整的权限校验模块2. **连接点Join Point** - 所有可能被拦截的方法3. **切入点Pointcut** - 真正被拦截的方法筛选规则4. **通知/增强Advice** - 拦截后执行的具体逻辑5. **目标对象Target** - 被代理的业务类6. **织入Weaving** - 将切面应用到目标对象四、完整工作流程图五、关键代码解读1. 获取当前请求的方法信息2. 执行原方法3. 获取注解参数六、动态代理原理Spring AOP 的两种代理方式七、概念关系图八、总结Spring AOP 核心概念详解一、Spring AOP 七大核心概念概念英文说明对应代码示例切面Aspect横切关注点的模块化把通用功能抽离出来Aspect标注的AuthInterceptor类连接点Join Point可以被拦截的方法执行点Controller/Service 中所有方法切入点Pointcut真正需要被拦截的连接点筛选条件annotation(authCheck)通知/增强Advice在切入点执行的逻辑doInterceptor()方法目标对象Target被代理的对象标注了AuthCheck的方法所在的类织入Weaving将切面应用到目标对象的过程Spring 运行时自动完成引入Introduction动态添加方法或字段不常用-二、用生活案例去理解AOP场景小区门禁系统想象一个小区的安保系统️ 小区 整个应用程序 每家每户 各个业务类Controller、Service 大门 方法入口 保安 切面Aspect 访客规则 切入点表达式Pointcut ✅❌ 检查流程 通知Advice保安切面的工作规则切入点只检查没有门禁卡的外卖员annotation(authCheck)检查通知登记信息、联系业主、决定是否放行住户目标对象小区里的每一户人家进入连接点任何人想通过大门的时刻三、示例代码详细解释1.切面Aspect- 完整的权限校验模块Aspect// 声明这是一个切面Component// 交给 Spring 管理publicclassAuthInterceptor{// 整个类就是一个切面封装了权限校验的所有逻辑}含义把权限校验这个横切关注点从业务代码中抽离出来类似工具类但它是自动作用的不需要手动调用为什么要抽离// ❌ 没有切面每个方法都要写重复的权限校验PostMapping(/delete)publicBaseResponsedeleteApp(longid){UserusercheckPermission();// 重复代码1if(!user.isAdmin()){// 重复代码2thrownewBusinessException(ErrorCode.NO_AUTH_ERROR);}// 真正的业务逻辑...}PostMapping(/update)publicBaseResponseupdateApp(Appapp){UserusercheckPermission();// 重复代码1if(!user.isAdmin()){// 重复代码2thrownewBusinessException(ErrorCode.NO_AUTH_ERROR);}// 真正的业务逻辑...}// ✅ 有切面业务代码只关心业务PostMapping(/delete)AuthCheck(mustRoleadmin)publicBaseResponsedeleteApp(longid){// 真正的业务逻辑...}2.连接点Join Point- 所有可能被拦截的方法// 这些都是连接点候选者ControllerpublicclassAppController{GetMapping(/list)// ← 连接点1publicListApplistApps(){}PostMapping(/delete)// ← 连接点2AuthCheck(mustRoleadmin)publicBooleandeleteApp(longid){}PostMapping(/update)// ← 连接点3AuthCheck(mustRoleadmin)publicBooleanupdateApp(Appapp){}}含义程序中所有可以插入切面逻辑的地方Spring AOP 中连接点就是方法的执行一个类的所有方法都是连接点3.切入点Pointcut- 真正被拦截的方法筛选规则Around(annotation(authCheck))// 这就是切入点表达式含义从所有连接点中筛选出真正需要拦截的方法是一个布尔表达式返回 true 的方法会被拦截你的代码中的切入点annotation(authCheck)规则匹配所有标注了AuthCheck注解的方法效果✅deleteApp()有AuthCheck→ 被拦截✅updateApp()有AuthCheck→ 被拦截❌listApps()没有AuthCheck→ 不被拦截其他常见的切入点表达式// 匹配 service 包下所有方法Around(execution(* com.example.service.*.*(..)))// 匹配所有以 delete 开头的方法Around(execution(* *.delete*(..)))// 匹配标注了 Transactional 的方法Around(annotation(org.springframework.transaction.annotation.Transactional))4.通知/增强Advice- 拦截后执行的具体逻辑Around(annotation(authCheck))// ← 通知类型 切入点publicObjectdoInterceptor(// ← 通知方法增强逻辑ProceedingJoinPointjoinPoint,AuthCheckauthCheck)throwsThrowable{// 前置处理 StringmustRoleauthCheck.mustRole();UserloginUseruserService.getLoginUser(request);// 权限校验逻辑 if(没有权限){thrownewBusinessException(ErrorCode.NO_AUTH_ERROR);}// 执行原方法 returnjoinPoint.proceed();}含义在切入点上具体做什么事包含了何时做通知类型和做什么方法体五种通知类型对比通知类型执行时机能否控制方法执行你的代码使用场景Around方法前后✅ 能权限校验可以阻止执行Before方法前❌ 不能日志记录AfterReturning成功返回后❌ 不能处理返回值AfterThrowing抛出异常后❌ 不能异常告警After方法完成后❌ 不能资源清理为什么选择Around// 因为权限校验需要控制方法是否执行if(没有权限){// 不调用 joinPoint.proceed()原方法不会执行thrownewBusinessException(ErrorCode.NO_AUTH_ERROR);}else{// 调用 proceed()原方法才会执行returnjoinPoint.proceed();}5.目标对象Target- 被代理的业务类ServicepublicclassAppServiceImplimplementsAppService{AuthCheck(mustRoleadmin)publicListAppVOgetAppVOList(ListAppappList){// 业务逻辑...}}含义被切面增强的类Spring 会为它创建代理对象6.织入Weaving- 将切面应用到目标对象编译前: 运行时: ┌──────────────┐ ┌──────────────────┐ │ AppServiceImpl │ │ Proxy 对象 │ │ │ │ ├─ 权限校验逻辑 │ ← 织入 │ getAppVOList │ │ ├─ 日志记录逻辑 │ ← 织入 └──────────────┘ │ └─ getAppVOList() │ └──────────────────┘含义把切面逻辑插入到目标方法的过程Spring AOP 采用运行时织入通过动态代理两种织入方式方式时机框架性能编译时织入编译期AspectJ更好运行时织入运行期Spring AOP稍差但灵活四、完整工作流程图① 应用启动 ↓ ② Spring 扫描到 Aspect 标注的类AuthInterceptor ↓ ③ 解析切入点表达式 annotation(authCheck) ↓ ④ 扫描所有 Bean找到标注了 AuthCheck 的方法 ↓ ⑤ 为这些 Bean 创建代理对象Proxy ↓ ⑥ 用户请求调用方法 ↓ ⑦ 实际调用的是代理对象 ↓ ⑧ 代理对象先执行切面逻辑doInterceptor ↓ ⑨ 权限校验通过 → 执行原方法 权限校验失败 → 抛异常不执行原方法五、关键代码解读1. 获取当前请求的方法信息ProceedingJoinPointjoinPoint作用封装了被拦截方法的所有信息// 可以获取joinPoint.getSignature().getName();// 方法名getAppVOListjoinPoint.getArgs();// 参数[appList]joinPoint.getTarget();// 目标对象AppServiceImpljoinPoint.getThis();// 代理对象Proxy2. 执行原方法returnjoinPoint.proceed();作用调用目标对象的原始方法可以调用多次重试机制可以不调用权限校验失败// 示例重试机制for(inti0;i3;i){try{returnjoinPoint.proceed();}catch(Exceptione){if(i2)throwe;}}3. 获取注解参数AuthCheckauthCheck// Spring 自动注入注解实例StringmustRoleauthCheck.mustRole();// 获取配置的权限要求原理Spring 读取方法上的注解自动传递给切面方法六、动态代理原理Spring AOP 的两种代理方式代理方式适用场景原理JDK 动态代理目标类实现了接口基于反射生成代理类CGLIB 代理目标类没有实现接口基于继承生成子类项目代码示例AppServiceImplimplementsAppService// 实现了接口↓Spring默认使用JDK动态代理代理对象伪代码// Spring 生成的代理类简化版classAppServiceImplProxyimplementsAppService{privateAppServiceImpltarget;privateAuthInterceptorauthInterceptor;OverridepublicListAppVOgetAppVOList(ListAppappList){// 1. 获取方法上的 AuthCheck 注解AuthCheckauthCheckgetAnnotation(getAppVOList);// 2. 调用切面逻辑returnauthInterceptor.doInterceptor(()-target.getAppVOList(appList),// joinPoint.proceed()authCheck);}}七、概念关系图切面Aspect 切入点Pointcut 通知Advice ↓ ↓ 决定拦截哪些方法 决定拦截后做什么 ↓ ↓ annotation() doInterceptor() ↓ ↓ ┌─────────────────────────────┐ │ 连接到一起织入 │ └─────────────────────────────┘ ↓ 作用于目标对象Target ↓ 在连接点Join Point执行八、总结概念是什么解决什么问题你的代码示例切面横切逻辑的模块代码复用、解耦AuthInterceptor类连接点可拦截的点定义候选集合所有 Controller/Service 方法切入点筛选规则精确匹配需要拦截的方法annotation(authCheck)通知具体执行逻辑定义拦截后做什么doInterceptor()方法目标对象被代理的类业务逻辑所在AppServiceImpl织入应用过程将切面逻辑插入目标Spring 自动完成切面 在哪里做切入点 做什么通知把通用功能权限、日志、事务从业务代码中抽离实现高内聚、低耦合。具体的使用方法请看下一篇文章Spring AOP(二) Spring AOP 完整指南

更多文章