news 2026/7/10 20:04:22

AOP(面向切面编程,Aspect-Oriented Programming)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AOP(面向切面编程,Aspect-Oriented Programming)

一、先搞懂:为什么需要 AOP?(解决什么痛点)

先看一个典型问题:假如你有 3 个业务方法(用户注册、订单创建、支付),每个方法都需要加「日志记录」「权限校验」「异常处理」逻辑,传统写法会这样:

java

运行

// 传统写法:通用逻辑和业务逻辑耦合 public void registerUser() { // 1. 日志记录(通用逻辑) log.info("开始执行用户注册"); // 2. 权限校验(通用逻辑) if (!hasPermission()) { throw new NoPermissionException(); } try { // 3. 核心业务逻辑 userService.save(); } catch (Exception e) { // 4. 异常处理(通用逻辑) log.error("注册失败", e); throw e; } } public void createOrder() { // 重复的日志、权限、异常逻辑... orderService.save(); }

痛点:通用逻辑(日志、权限)重复写,代码冗余,维护成本高(比如要改日志格式,得改所有方法)。

AOP 的解决思路:把日志、权限这些通用逻辑抽成「切面」,在不修改业务代码的前提下,自动 “织入” 到业务方法的执行环节(比如执行前、执行后、抛出异常时)。


二、AOP 核心概念(新手必记)

用 “切蛋糕” 类比:业务逻辑是蛋糕的主体,AOP 是 “刀”,按指定规则(切点)切到蛋糕的指定位置(连接点),把切面逻辑(奶油 / 水果)加进去。

概念通俗解释
切面(Aspect)抽离出来的通用逻辑模块(比如日志切面、权限切面),包含「切点」+「通知」
连接点(Join Point)程序执行过程中的 “时机点”(比如方法执行前、执行后、抛出异常时)
切点(Pointcut)筛选连接点的 “规则”(比如只匹配 service 包下的所有方法)
通知(Advice)切面在连接点执行的具体逻辑(比如前置通知:方法执行前打日志)
织入(Weaving)把切面逻辑植入到业务代码的过程(AOP 框架自动完成,分编译期、类加载期、运行期)
常见的通知类型(核心):
  1. 前置通知(Before):业务方法执行前执行(比如权限校验);
  2. 后置通知(After):业务方法执行后执行(无论是否异常,比如清理资源);
  3. 返回通知(AfterReturning):业务方法正常返回后执行(比如记录方法返回值);
  4. 异常通知(AfterThrowing):业务方法抛出异常后执行(比如异常日志);
  5. 环绕通知(Around):包裹业务方法执行(可控制方法是否执行、修改参数 / 返回值,最灵活)。

三、AOP 实现原理(两种核心方式)

AOP 本身是思想,主流实现有两种:

1. 静态代理(编译期织入)
  • 原理:通过修改字节码实现(比如 AspectJ),编译时直接把切面逻辑编译到业务类的字节码中;
  • 特点:性能高(运行时无额外开销),但需要特殊编译器;
  • 适用:对性能要求极高的场景。
2. 动态代理(运行期织入)
  • 原理:运行时动态生成业务类的代理对象,调用业务方法时先执行切面逻辑(Spring AOP 默认方式);
  • 两种实现:
    • JDK 动态代理:基于接口,只能代理实现了接口的类;
    • CGLIB 动态代理:基于继承,可代理任意类(即使没实现接口);
  • 特点:无需修改字节码,灵活,但运行时有轻微性能损耗(可忽略);
  • 适用:大部分业务场景(Spring AOP 首选)。

四、Spring AOP 实战示例(新手能跑通)

Spring AOP 是最常用的 AOP 框架,下面用「日志切面」演示核心用法:

前置条件
  1. 引入 Spring AOP 依赖(Maven):

xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
步骤 1:定义切面类(核心)

java

运行

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; // 1. 标记为组件(Spring扫描)+ 标记为切面 @Component @Aspect public class LogAspect { // 2. 定义切点:匹配com.example.service包下所有类的所有方法 @Pointcut("execution(* com.example.service.*.*(..))") public void servicePointcut() {} // 3. 前置通知:方法执行前打日志 @Before("servicePointcut()") public void beforeAdvice(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); // 获取方法名 Object[] args = joinPoint.getArgs(); // 获取方法参数 System.out.println("前置通知:执行方法[" + methodName + "],参数:" + args); } // 4. 环绕通知:统计方法执行耗时 @Around("servicePointcut()") public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); // 执行原业务方法 Object result = joinPoint.proceed(); long cost = System.currentTimeMillis() - start; System.out.println("环绕通知:方法[" + joinPoint.getSignature().getName() + "]耗时" + cost + "ms"); return result; } // 5. 异常通知:方法抛异常时打日志 @AfterThrowing(pointcut = "servicePointcut()", throwing = "e") public void afterThrowingAdvice(JoinPoint joinPoint, Exception e) { String methodName = joinPoint.getSignature().getName(); System.out.println("异常通知:方法[" + methodName + "]抛出异常:" + e.getMessage()); } }
步骤 2:定义业务类

java

运行

import org.springframework.stereotype.Service; @Service public class UserService { public void register(String username) { System.out.println("核心业务:用户[" + username + "]注册成功"); // 模拟异常(可注释测试) // if (username.isEmpty()) { // throw new RuntimeException("用户名不能为空"); // } } }
步骤 3:测试执行

java

运行

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class AopDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(AopDemoApplication.class, args); UserService userService = context.getBean(UserService.class); userService.register("张三"); // 调用业务方法 } }
输出结果

plaintext

前置通知:执行方法[register],参数:[张三] 核心业务:用户[张三]注册成功 环绕通知:方法[register]耗时1ms

五、AOP 典型使用场景

  1. 日志记录:接口调用日志、方法执行日志、异常日志(最常用);
  2. 权限校验:接口访问前校验用户权限,无权限则拦截;
  3. 事务管理:方法执行前开启事务,执行成功提交,失败回滚(Spring 事务底层就是 AOP);
  4. 性能监控:统计方法执行耗时、接口响应时间;
  5. 缓存控制:方法执行前查缓存,有则返回,无则执行方法并缓存结果;
  6. 异常统一处理:集中捕获业务异常,统一返回格式。

总结

  1. AOP 的核心是解耦:把通用逻辑(日志、权限)从业务逻辑中抽离,实现 “一处定义,处处使用”;
  2. Spring AOP 是主流实现,基于动态代理,通过「切面 + 切点 + 通知」完成逻辑织入;
  3. 高频场景:日志、权限、事务、性能监控,是企业级开发的必备技能。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 11:07:46

【金融APP智能化升级】:基于Open-AutoGLM的账单查询优化五步法

第一章&#xff1a;金融APP智能化升级背景与挑战随着移动互联网和人工智能技术的迅猛发展&#xff0c;传统金融服务模式正经历深刻变革。用户对金融APP的期望已从基础的账户查询、转账支付&#xff0c;逐步转向个性化推荐、智能风控、实时客服和自动化理财等高阶功能。在此背景…

作者头像 李华
网站建设 2026/7/9 12:30:07

Open-AutoGLM边缘计算实战(资源占用降低60%的秘诀)

第一章&#xff1a;Open-AutoGLM边缘计算部署优化概述 在物联网与人工智能融合发展的背景下&#xff0c;Open-AutoGLM作为一款轻量级自动化生成语言模型&#xff0c;正逐步被应用于边缘设备的智能推理场景。由于边缘计算资源受限、网络波动频繁&#xff0c;如何高效部署并优化该…

作者头像 李华
网站建设 2026/7/10 11:10:10

2025 年网络安全就业指南:人才缺口超 200 万,这些方向最吃香

在数字经济时代&#xff0c;网络安全早已不是 “技术冷门”&#xff0c;而是关系到每个人、每家企业、每个国家的核心领域。从个人隐私泄露到国家级网络攻击&#xff0c;从企业数据被盗到关键基础设施瘫痪&#xff0c;网络安全的重要性正以肉眼可见的速度攀升。如果你正在考虑职…

作者头像 李华
网站建设 2026/7/9 19:33:51

基于机器学习的信贷反欺诈系统的设计与实现任务书

毕业论文&#xff08;设计&#xff09;任务书题 目姓 名学 院专 业班 级学 号校内指导教师职称校外指导教师职称年 月 日任务书填写要求1&#xff0e;毕业论文&#xff08;设计&#xff09;任务书由指导教师根据各课题的具体情况填写&#xff0c;经学生所在学院…

作者头像 李华
网站建设 2026/7/10 6:21:42

基于计算机视觉的景区智能监测系统设计与实现课题申报

附表A.1&#xff1a;六盘水师范学院毕业论文&#xff08;设计&#xff09;课题申报审核表&#xff08;20届&#xff09;学院计算机科学学院适用专业计算机科学与技术论文&#xff08;设计&#xff09;题目基于计算机视觉的景区智能监测系统设计与实现指导教师姓名石云指导教师职…

作者头像 李华
网站建设 2026/7/10 0:27:59

30V 低压侧过压保护芯片 集成28毫欧低内阻开关 XR1802

XR1802是一款低压侧过压保护芯片&#xff0c;芯片最高 耐压高达 30V&#xff0c;同时集成了 28 毫欧的低内阻开关 NMOS&#xff0c;可以很大程度降低输入压降并降低内部损耗 与发热&#xff0c;芯片的过压保护值可以通过外部电阻灵活设 置&#xff0c;外围应用电路非常简单。产…

作者头像 李华