什么是 AOP(面向切面编程)?Spring AOP 中常用的概念(如切面、连接点、通知、切点)分别是什么意思?
·
AOP(面向切面编程)概述
AOP(Aspect-Oriented Programming) 是一种编程范式,通过将横切关注点从业务逻辑中分离出来,实现代码的模块化和复用。
核心思想
- 将分散在多个模块中的相同逻辑(如日志、事务、权限校验)提取到一个独立的模块中
- 通过声明式的方式将这些逻辑"织入"到业务代码中
- 减少重复代码,提高可维护性
Spring AOP 核心概念
1. 切面
- 封装横切逻辑的模块
- 是一个类,包含通知和切点
- 使用
@Aspect注解标识
@Aspect
@Component
public class LoggingAspect {
// 切面类,包含通知和切点
}
2. 连接点
- 程序执行的某个特定位置
- 在 Spring AOP 中,连接点通常是方法的执行
- 理论上可以是字段访问、异常处理等,但 Spring 只支持方法级别的连接点
// 下面所有方法的执行都是潜在的连接点
public class UserService {
public void createUser() { } // 连接点
public void updateUser() { } // 连接点
public void deleteUser() { } // 连接点
}
3. 通知
- 在连接点执行的代码
- 定义了"何时"以及"如何"执行切面逻辑
| 通知类型 | 说明 | 注解 |
|---|---|---|
| 前置通知 | 方法执行前 | @Before |
| 后置通知 | 方法执行后(无论成功或失败) | @After |
| 返回通知 | 方法成功返回后 | @AfterReturning |
| 异常通知 | 方法抛出异常后 | @AfterThrowing |
| 环绕通知 | 包裹方法执行,可控制是否执行 | @Around |
@Aspect
@Component
public class LoggingAspect {
// 前置通知
@Before("execution(* com.example.service.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法执行前: " + joinPoint.getSignature().getName());
}
// 后置通知
@After("execution(* com.example.service.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("方法执行后: " + joinPoint.getSignature().getName());
}
// 返回通知
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("方法返回: " + result);
}
// 异常通知
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("方法异常: " + ex.getMessage());
}
// 环绕通知(最强大)
@Around("execution(* com.example.service.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知 - 前");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕通知 - 后");
return result;
}
}
4. 切点
- 匹配连接点的表达式
- 定义了"在哪里"执行通知
- 使用 AspectJ 切点表达式语言
@Aspect
@Component
public class LoggingAspect {
// 切点定义
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 复用切点
@Before("serviceLayer()")
public void beforeServiceMethod() {
System.out.println("Service 层方法执行前");
}
}
切点表达式示例
// 匹配所有 public 方法
execution(public * *(..))
// 匹配所有以 set 开头的方法
execution(* set*(..))
// 匹配 UserService 类的所有方法
execution(* com.example.UserService.*(..))
// 匹配 service 包下所有类的所有方法
execution(* com.example.service.*.*(..))
// 匹配 service 包及其子包下所有类的所有方法
execution(* com.example.service..*.*(..))
// 匹配第一个参数是 String 的方法
execution(* *(String, ..))
// 匹配所有带 @Transactional 注解的方法
@annotation(org.springframework.transaction.annotation.Transactional)
完整示例
@Aspect
@Component
public class TransactionAspect {
// 切点:匹配 service 包下所有方法
@Pointcut("execution(* com.example.service..*.*(..))")
public void serviceMethods() {}
// 环绕通知:事务管理
@Around("serviceMethods()")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开启事务");
try {
Object result = joinPoint.proceed();
System.out.println("提交事务");
return result;
} catch (Exception e) {
System.out.println("回滚事务");
throw e;
}
}
}
概念关系图
┌─────────────────────────────────────────────────────────┐
│ 切面 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 通知(Advice) │ │
│ │ - @Before │ │
│ │ - @After │ │
│ │ - @Around │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 切点(Pointcut) │ │
│ │ execution(* com.example.service.*.*(..)) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
│ 匹配
▼
┌─────────────────────────────────────────────────────────┐
│ 连接点(JoinPoint) │
│ UserService.createUser() ──── 目标方法执行 │
│ UserService.updateUser() ──── 目标方法执行 │
└─────────────────────────────────────────────────────────┘
Spring AOP vs AspectJ
| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 实现方式 | 代理模式(JDK 动态代理 / CGLIB) | 编译期/类加载期织入 |
| 支持的连接点 | 仅方法 | 方法、字段、构造器等 |
| 性能 | 稍低(代理调用) | 更高(直接织入) |
| 复杂度 | 简单,Spring 集成 | 复杂,需要额外配置 |
| 适用场景 | 大多数业务场景 | 需要细粒度控制时 |
总结
| 概念 | 作用 | 问题 |
|---|---|---|
| 切面 | 封装横切逻辑的模块 | 是什么? |
| 连接点 | 程序执行的特定位置(方法) | 在哪里? |
| 通知 | 在连接点执行的代码 | 何时做?如何做? |
| 切点 | 匹配连接点的表达式 | 在哪些连接点? |
更多推荐

所有评论(0)