SpringBoot 中的 AOP(Aspect-Oriented Programming,面向切面编程)是一种通过分离横切关注点(如日志、事务、权限校验等)来提高代码复用性和可维护性的编程思想。它可以在不修改原有业务逻辑代码的前提下,对方法进行增强(如前置处理、后置处理、异常处理等)。
一、AOP 核心概念
- 切面(Aspect)
封装横切关注点的类,包含了通知(增强逻辑)和切点(拦截规则)的定义。
例:@Aspect
注解标记的类就是一个切面。 通知(Advice)
切面中具体的增强逻辑,定义了在“何时”执行增强操作。常见类型:@Before
:目标方法执行前执行@After
:目标方法执行后执行(无论是否异常)@AfterReturning
:目标方法正常返回后执行@AfterThrowing
:目标方法抛出异常后执行@Around
:环绕目标方法,可自定义执行时机(最灵活,需手动调用目标方法)
- 切点(Pointcut)
定义拦截哪些方法的规则,通过表达式(如execution
)指定目标方法。
例:execution(* com.example.service.*.*(..))
表示拦截com.example.service
包下所有类的所有方法。 - 连接点(JoinPoint)
程序执行过程中可被拦截的点(如方法调用、异常抛出等),是切点的实际执行位置。 - 目标对象(Target)
被切面拦截并增强的原始对象(业务逻辑类)。
例:execution(* com.example.service.*.*(..))
表示com.example.service
包下所有类的所有方法。 - 代理对象(Proxy)
AOP 生成的、包含目标对象和增强逻辑的代理实例,实际执行时通过代理对象调用目标方法。
二、AOP 实现原理
SpringBoot 的 AOP 基于动态代理实现:
- 若目标类实现了接口:默认使用 JDK 动态代理(通过接口生成代理类)。
- 若目标类未实现接口:使用 CGLIB 动态代理(通过继承目标类生成代理类)。
(SpringBoot 2.x 后默认支持 CGLIB,无需额外配置)
三、SpringBoot 中使用 AOP 的步骤
引入依赖
在pom.xml
中添加 AOP starter(SpringBoot 已整合,无需手动配置代理):<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
定义切面类
- 用
@Aspect
标记类为切面。 - 用
@Component
注册为 Spring 组件。
- 用
定义切点
用@Pointcut
注解配合表达式定义拦截规则,常见表达式:execution
:匹配方法执行(最常用),语法:execution(修饰符 返回值 包名.类名.方法名(参数))
例:execution(* com.example.service.UserService.*(..))
拦截UserService
类的所有方法。@annotation
:匹配被指定注解标记的方法
例:@annotation(com.example.annotation.Log)
拦截所有被@Log
注解标记的方法。
execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
精确匹配DeptServiceImpl
类中public、无返回值、名为delete
且参数为一个Integer的方法。execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
类似上一条,但不限制方法访问修饰符(public/private/protected等)。execution(void delete(java.lang.Integer))
匹配任意类中所有无返回值、名为delete
且参数为Integer的方法。省略包名和类名会导致匹配范围过大,不推荐使用。execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
匹配DeptServiceImpl
类中任意返回类型、名为delete
且参数为Integer的方法。execution(* com.*.service.impl.DeptServiceImpl.delete(java.lang.Integer))
匹配com
包下任意一级子包(如com.foo
、com.bar
)中DeptServiceImpl
类的delete
方法。execution(* com.itheima.service.impl.*.delete(java.lang.Integer))
匹配DeptServiceImpl
类所在包中任意类的delete
方法(参数为Integer)。execution(* com.itheima.service.impl.*.*(java.lang.Integer))
匹配DeptServiceImpl
类所在包中任意类的任意方法,但要求参数必须是一个Integer。execution(* com.itheima.service.impl.*.*(*))
匹配DeptServiceImpl
类所在包中任意类的任意方法,但要求必须有且仅有一个参数(类型不限)。execution(* com.itheima.service.impl.*.del*(*))
匹配DeptServiceImpl
类所在包中任意类的以del
开头的方法,且方法必须有一个参数(类型不限)。execution(* com.itheima.service.impl.*.*e(*))
匹配DeptServiceImpl
类所在包中任意类的以e
结尾的方法,且方法必须有一个参数(类型不限)。execution(* com..service.impl.DeptServiceImpl.*(..))
匹配com
包下任意层级子包(如com.foo.bar
)中DeptServiceImpl
类的任意方法(参数不限)。execution(* com.itheima.service.*.*(..))
匹配com.itheima.service
包下所有类的所有方法(参数不限)。组合表达式
@Before("execution(* com.itheima.service.impl.DeptServiceImpl.list(..)) || " + "execution(* com.itheima.service.impl.DeptServiceImpl.delete(..))")
同时匹配
DeptServiceImpl
类中的list
方法和delete
方法(任意参数、任意返回类型)。
关键符号总结:
*
:匹配任意返回类型、任意包名/类名/方法名(单个部分)。..
:匹配任意层级子包或任意数量、类型的参数。||
:逻辑或,组合多个切入点。
- 定义通知
在切面类中用通知注解(如@Before
、@AfterReturning
等)定义增强逻辑。
四、示例代码
// 1. 定义切面类
@Aspect
@Component
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 + " 开始执行,参数:" + Arrays.toString(args));
}
// 后置返回通知:方法正常返回后打印结果
@AfterReturning(pointcut = "servicePointcut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 执行结束,返回值:" + result);
}
}
五、AOP 的常见应用场景
- 日志记录:自动记录方法调用、参数、返回值等。
- 事务管理:通过
@Transactional
注解(底层基于 AOP)控制事务的提交/回滚。 - 权限校验:方法执行前验证用户权限,无权限则拦截。
- 异常处理:统一捕获方法抛出的异常并处理(如返回友好提示)。
- 性能监控:统计方法执行耗时,分析性能瓶颈。
六、注意事项
- AOP 拦截的是Spring 容器管理的Bean,非 Spring 管理的对象无法被增强。
- 同一目标方法被多个切面拦截时,可通过
@Order
注解指定切面执行顺序(值越小越先执行)。 @Around
通知需手动调用ProceedingJoinPoint.proceed()
执行目标方法,否则目标方法不会执行。
通过 AOP,SpringBoot 可以优雅地实现业务逻辑与横切逻辑的解耦,大幅提升代码的可维护性。