💡 一则也许对你有用的小广告 🏆
欢迎飞飞程序员 ,你将获得:专属的实战项目(已更新的所有会员标识的项目都能学习) / 1v1 提问 / Java 学习路线 / PHP 学习路线 / 学习打卡 / 社群讨论
截止目前, 飞飞 正在疯狂爆肝实战项目,后续还会上新更多项目,目标是将所学知识开发成项目并且分享给大家,如知识付费系统, Ai系统, CMS系统,在线商城系统,等等 ,欢迎点击围观
AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,它是对 OOP(面向对象编程) 的补充。
简单来说,OOP 关注的是纵向的职责划分(比如把系统拆分成 Controller、Service、Mapper 等不同的类),而 AOP 关注的是横向的共性功能抽取(比如日志、事务、权限校验等,这些功能散落在各个业务方法中)。
AOP 的核心目标是:将横切关注点(Cross-Cutting Concerns)从业务逻辑中分离出来,提高代码的复用性和可维护性。
1. 为什么需要 AOP?
假设你有一个用户服务,里面有 注册、登录、修改密码 等方法。现在产品经理要求:每个方法执行前都要记录日志,执行后都要统计耗时。
没有 AOP 时(OOP 写法):
public class UserService {
public void register() {
long start = System.currentTimeMillis();
System.out.println("开始执行 register");
// ... 核心业务逻辑
System.out.println("执行结束,耗时:" + (System.currentTimeMillis() - start));
}
public void login() {
long start = System.currentTimeMillis();
System.out.println("开始执行 login");
// ... 核心业务逻辑
System.out.println("执行结束,耗时:" + (System.currentTimeMillis() - start));
}
// ... 每个方法都要重复写这些日志代码
}
问题:日志代码和业务代码耦合在一起,大量重复,修改时要改很多地方。
使用 AOP 后:
@Service
public class UserService {
public void register() {
// ... 只写核心业务逻辑
}
public void login() {
// ... 只写核心业务逻辑
}
}
日志和耗时统计被抽取到一个切面中,Spring 会在运行时自动把这些逻辑“织入”到目标方法前后。
2. AOP 的核心概念
| 术语 | 英文 | 说明 |
| 切面 | Aspect | 横切关注点的模块化,比如一个日志切面类。 |
| 连接点 | Join Point | 程序执行过程中的某个点,比如方法调用、异常抛出。Spring AOP 只支持方法级别的连接点。 |
| 切点 | Pointcut | 匹配连接点的表达式,决定在哪些方法上应用切面。 |
| 通知 | Advice | 在连接点上执行的动作,比如“方法执行前打印日志”。 |
| 目标对象 | Target | 被切面织入的原始对象,也就是业务类。 |
| 代理 | Proxy | Spring AOP 通过动态代理生成的对象,包裹了目标对象。 |
| 织入 | Weaving | 将切面应用到目标对象并创建代理对象的过程。 |
3. 五种通知类型
Spring AOP 提供了 5 种通知(Advice):
| 通知类型 | 注解 | 执行时机 |
| 前置通知 | @Before | 目标方法执行前 |
| 后置通知 | @After | 目标方法执行后(无论是否异常) |
| 返回通知 | @AfterReturning | 目标方法正常返回后 |
| 异常通知 | @AfterThrowing | 目标方法抛出异常后 |
| 环绕通知 | @Around | 目标方法执行前后都能控制,最强大 |
4. 一个完整的 Spring Boot AOP 示例
第一步:引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:编写切面类
@Aspect
@Component
public class LogAspect {
// 定义切点:匹配 com.example.service 包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {}
// 前置通知
@Before("servicePointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("【前置】开始执行:" + joinPoint.getSignature().getName());
}
// 返回通知
@AfterReturning(pointcut = "servicePointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("【返回】执行成功,返回值:" + result);
}
// 异常通知
@AfterThrowing(pointcut = "servicePointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
System.out.println("【异常】执行出错:" + e.getMessage());
}
// 环绕通知(最常用,可以统计耗时)
@Around("servicePointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("【环绕】方法 " + pjp.getSignature().getName() + " 耗时:" + (end - start) + "ms");
return result;
}
}
第三步:业务类(无需任何改动)
@Service
public class UserService {
public String register(String username) {
System.out.println("正在注册用户:" + username);
return "注册成功";
}
}
运行结果:
【前置】开始执行:register
正在注册用户:张三
【返回】执行成功,返回值:注册成功
【环绕】方法 register 耗时:2ms
5. 切点表达式(Pointcut Expression)
最常用的是 execution 表达式,语法如下:
execution(修饰符 返回值类型 包名.类名.方法名(参数类型))
常见示例:
| 表达式 | 含义 |
execution(* com.example.service.*.*(..)) | service 包下所有类的所有方法 |
execution(public * *(..)) | 所有 public 方法 |
execution(* com.example..*.*(..)) | com.example 及其子包下所有方法 |
@annotation(com.example.annotation.Log) | 所有标注了 @Log 注解的方法 |
6. Spring AOP 的实现原理
Spring AOP 底层使用动态代理:
| 代理方式 | 条件 |
| JDK 动态代理 | 目标类实现了接口(默认) |
| CGLIB 代理 | 目标类没有实现接口(Spring Boot 默认使用 CGLIB) |
Spring Boot 2.x 之后,默认统一使用 CGLIB 代理,即使有接口也用 CGLIB。
7. AOP 的典型应用场景
| 场景 | 说明 |
| 日志记录 | 统一记录方法调用日志、耗时。 |
| 事务管理 | @Transactional 就是 AOP 的经典应用。 |
| 权限校验 | 方法执行前检查用户是否有权限。 |
| 缓存 | @Cacheable 也是通过 AOP 实现的。 |
| 异常处理 | 统一捕获并处理异常。 |
| 参数校验 | 方法执行前校验参数合法性。 |
8. AOP vs OOP
| 对比项 | OOP | AOP |
| 关注点 | 纵向:类、对象、继承 | 横向:切面、通知 |
| 解决问题 | 业务逻辑的模块化 | 横切关注点的模块化 |
| 典型场景 | Service、Controller 分层 | 日志、事务、权限 |
| 关系 | 基础 | 补充,不替代 OOP |
总结
| 特性 | 说明 |
| 定义 | 面向切面编程,对 OOP 的补充 |
| 核心思想 | 将横切关注点从业务逻辑中分离 |
| 核心概念 | 切面、切点、通知、连接点、代理 |
| 通知类型 | @Before、@After、@AfterReturning、@AfterThrowing、@Around |
| 实现原理 | 动态代理(JDK 或 CGLIB) |
| 典型应用 | 日志、事务、权限、缓存 |
| Spring Boot 依赖 | spring-boot-starter-aop |
一句话:AOP 就是让你在不修改业务代码的前提下,给方法“偷偷”加上额外功能的技术。