spring-aop
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Spring AOP是Spring框架中实现面向切面编程的核心模块, 作用 分离关注点
| 术语 | 解释 | 代码示例 |
|---|---|---|
| 切面(Aspect) | 横切关注点的模块化(一个类) | @Aspect注解的类 |
| 连接点(Join Point) | 程序执行中的点(如方法调用) | OrderService.createOrder() |
| 通知(Advice) | 切面在特定连接点执行的动作 | @Before, @After, @Around |
| 切入点(Pointcut) | 匹配连接点的表达式 | execution(* com..service.*.*(..)) |
@Aspect
@Component
public class CompleteAspect {
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}
// 1. 前置通知 - 方法执行前 可以获取入参joinPoint.getArgs() + 方法名
@Before("userServiceMethods()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("【前置】准备执行: " +
joinPoint.getSignature().getName());
}
// 2. 后置通知 - 方法执行后(无论是否异常)
@After("userServiceMethods()")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("【后置】方法执行完成: " +
joinPoint.getSignature().getName());
}
// 3. 返回通知 - 方法正常返回后 returning指定接收返回值的变量名
@AfterReturning(
pointcut = "userServiceMethods()",
returning = "result"
)
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("【返回】方法返回: " + result);
}
// 4. 异常通知 - 方法抛出异常后 throwing指定接收异常的变量名
@AfterThrowing(
pointcut = "userServiceMethods()",
throwing = "ex"
)
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
System.out.println("【异常】方法抛出异常: " + ex.getMessage());
}
// 5. 环绕通知 - 最强大,控制整个执行过程【可以修改目标方法入参 拦截目标方法执行 修改目标方法返参】
@Around("userServiceMethods()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("【环绕前】");
// 可以修改参数
Object[] args = joinPoint.getArgs();
if (args.length > 0 && args[0] instanceof String) {
args[0] = ((String) args[0]).toUpperCase();
}
// 执行原方法(可以选择不执行!)
Object result = joinPoint.proceed(args);
// 可以修改返回值
if (result instanceof String) {
result = "修改后的: " + result;
}
System.out.println("【环绕后】");
return result;
}
}
常用的切入点表达式
@Aspect
public class PointcutExamples {
// 1. 按方法执行 第1个*匹配所有的返回值类型 第2个*匹配所有的方法名 ..表示参数个数不限定
@Pointcut("execution(* com.example.service.*(..))")
public void allPublicServiceMethods() {} // service包下所有方法
// 2. 按注解 注解的全限定类名
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {} // 所有@Transactional方法
- execution 匹配规则
全写法:[public] 返回值类型 [全限定类路径].add(入参1类型, 入参2类型)
通配符:
* 表示任意字符
.. 在参数位置,表示多个任意类型参数(包含0个); 在类型位置表示多个层级
通知方法的执行顺序
正常链路 前置通知@Before --- 目标方法 --- 返回通知@AfterReturning --- 后置通知@After
异常链路 前置通知@Before --- 目标方法 --- 异常通知@AfterThrowing --- 后置通知@After
@Around前半 → @Before → 目标方法 → @AfterReturning/@AfterThrowing → @After → @Around后半
多个切面的执行顺序
@Order决定切面层级, 数值越小,优先级越高,越在外层
@Aspect @Order(1) // 第一层
public class Aspect1 {
@Before("pointcut()")
public void before1() { System.out.println("Aspect1-Before"); }
@Around("pointcut()")
public Object around1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Aspect1-Around前");
Object result = pjp.proceed(); // 进入下一层
System.out.println("Aspect1-Around后");
return result;
}
@After("pointcut()")
public void after1() { System.out.println("Aspect1-After"); }
}
@Aspect @Order(2) // 第二层
public class Aspect2 {
@Before("pointcut()")
public void before2() { System.out.println("Aspect2-Before"); }
@Around("pointcut()")
public Object around2(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Aspect2-Around前");
Object result = pjp.proceed(); // 进入下一层
System.out.println("Aspect2-Around后");
return result;
}
@After("pointcut()")
public void after2() { System.out.println("Aspect2-After"); }
}
@Aspect @Order(3) // 第三层(最内层)
public class Aspect3 {
@Before("pointcut()")
public void before3() { System.out.println("Aspect3-Before"); }
@Around("pointcut()")
public Object around3(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Aspect3-Around前");
Object result = pjp.proceed(); // 执行目标方法
System.out.println("Aspect3-Around后");
return result;
}
@After("pointcut()")
public void after3() { System.out.println("Aspect3-After"); }
}
- 上述执行顺序图示
1. Aspect1.around前 → Aspect1.before
2. ↓ Aspect2.around前 → Aspect2.before
3. ↓ Aspect3.around前 → Aspect3.before
4. ↓ 目标方法执行
5. ↑ Aspect3.after → Aspect3.around后
6. ↑ Aspect2.after → Aspect2.around后
7. ↑ Aspect1.after → Aspect1.around后
- 实际输出结果
Aspect1-Around前 // 最外层开始
Aspect1-Before
Aspect2-Around前 // 第二层
Aspect2-Before
Aspect3-Around前 // 最内层
Aspect3-Before
>>> 目标方法执行 <<< // 到达核心
Aspect3-After // 从最内层开始返回
Aspect3-Around后
Aspect2-After
Aspect2-Around后
Aspect1-After
Aspect1-Around后 // 最外层结束
应用举例 权限控制
- 编写权限注解
// 自定义权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String value(); // 需要的权限,如 "user:delete"
}
- 编写切面类
// 权限切面
@Aspect
@Component
public class SecurityAspect {
@Around("@annotation(requirePermission)")
public Object checkPermission(ProceedingJoinPoint joinPoint,
RequirePermission requirePermission) throws Throwable {
// 获取注解的值
String requiredPerm = requirePermission.value();
// 获取当前用户的权限
User user = getCurrentUser();
if (!user.hasPermission(requiredPerm)) {
throw new AccessDeniedException("权限不足: " + requiredPerm);
}
return joinPoint.proceed();
}
}
- 在控制层
注解标记需要权限校验的接口
// 使用:优雅的权限控制
@RestController
public class UserController {
@DeleteMapping("/users/{id}")
@RequirePermission("user:delete") // 只需一个注解!
public void deleteUser(@PathVariable Long id) {
// 纯业务逻辑
}
}