spring boot aop简单demo
是一种编程范式,用于将横切关注点(如日志、事务、权限校验)与业务逻辑解耦。通过动态代理技术,Spring Boot AOP 可以在不修改源代码的情况下,在方法执行前后插入通用逻辑。合理使用 AOP 能显著提升代码的可维护性和复用性,但需注意代理机制的限制(如内部方法调用问题)。Spring AOP 基于。
·
Spring Boot AOP 的核心概念
AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,用于将横切关注点(如日志、事务、权限校验)与业务逻辑解耦。通过动态代理技术,Spring Boot AOP 可以在不修改源代码的情况下,在方法执行前后插入通用逻辑。
AOP 的五大核心应用场景
| 场景 | 说明 | 典型实现 |
|---|---|---|
| 日志记录 | 统一记录方法入参、返回值、异常信息 | @Around 环绕通知 |
| 事务管理 | 声明式事务(如 @Transactional) |
Spring 内置事务切面 |
| 权限校验 | 拦截请求,验证用户角色或权限 | @Before 前置通知 + 自定义注解 |
| 性能监控 | 统计方法执行耗时 | @Around + 计时逻辑 |
| 缓存处理 | 方法执行前检查缓存,存在则直接返回 | @Around + Redis 操作 |
| 异常统一处理 | 全局捕获异常并返回标准化错误响应 | @AfterThrowing 异常通知 |
Spring Boot AOP 实现步骤
1. 添加依赖
在 pom.xml 中引入 AOP 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建切面类
使用 @Aspect 和 @Component 定义切面:
package com.example.spring_demo01.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
// 定义切点:拦截所有 Service 层方法
@Pointcut("execution(* com.example.spring_demo01.service.*.*(..))")
public void serviceLayer() {}
// 环绕通知:记录方法耗时
@Around("serviceLayer()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标方法
long duration = System.currentTimeMillis() - startTime;
System.out.printf("方法 %s 执行耗时: %d ms\n", joinPoint.getSignature(), duration);
return result;
}
}
3. 定义自定义注解(可选)
创建注解用于标记需要特殊处理的方法:
package com.example.spring_demo01.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {
}
4. 实现权限校验切面
@Aspect
@Component
public class AuthAspect {
// 拦截所有标记了 @AdminOnly 的方法
@Before("@annotation(com.example.spring_demo01.annotation.AdminOnly)")
public void checkAdminPermission() {
// 模拟权限校验
if (!CurrentUser.isAdmin()) {
throw new RuntimeException("无权限访问!");
}
}
}
5. 在业务方法上使用注解
@Service
public class UserService {
@AdminOnly
public void deleteUser(Long userId) {
// 删除用户逻辑
}
}
AOP 核心注解详解
| 注解 | 说明 |
|---|---|
@Aspect |
声明一个切面类 |
@Pointcut |
定义切点表达式,确定哪些方法需要被拦截 |
@Before |
在目标方法执行前执行 |
@After |
在目标方法执行后执行(无论是否抛出异常) |
@AfterReturning |
在目标方法成功返回后执行 |
@AfterThrowing |
在目标方法抛出异常后执行 |
@Around |
环绕通知,可完全控制目标方法的执行 |
切点表达式语法
| 表达式示例 | 说明 |
|---|---|
execution(* com.example.service.*.*(..)) |
匹配 service 包下所有类的所有方法 |
@annotation(com.example.AdminOnly) |
匹配标记了 @AdminOnly 注解的方法 |
within(com.example.controller..*) |
匹配 controller 包及其子包下的所有方法 |
args(java.lang.String, ..) |
匹配第一个参数为 String 类型的方法 |
AOP 实现原理
Spring AOP 基于 动态代理 实现:
- JDK 动态代理:针对实现了接口的类,通过
Proxy类生成代理对象。 - CGLIB 代理:针对未实现接口的类,通过继承生成子类代理。
常见问题与解决方案
问题 1:AOP 不生效
- 原因:同类内部方法调用(如
this.method())无法被代理拦截。 - 解决:通过依赖注入调用自身方法:
@Service public class UserService { @Autowired private UserService self; // 注入自身代理对象 public void methodA() { self.methodB(); // 通过代理对象调用 } @AdminOnly public void methodB() {} }
问题 2:切点表达式错误
- 调试工具:使用
AopUtils检查方法是否被代理:boolean isProxy = AopUtils.isAopProxy(bean);
问题 3:循环依赖
- 原因:AOP 代理导致 Bean 初始化顺序冲突。
- 解决:使用
@Lazy延迟注入:@Service public class OrderService { @Lazy @Autowired private UserService userService; }
总结
通过 Spring Boot AOP,你可以高效地实现以下功能:
- 统一日志记录
- 声明式事务管理
- 细粒度权限控制
- 接口性能监控
- 全局异常处理
合理使用 AOP 能显著提升代码的可维护性和复用性,但需注意代理机制的限制(如内部方法调用问题)。
更多推荐
所有评论(0)