在日常开发中,我们经常需要给方法添加一些横切关注点(Cross-Cutting Concerns),比如日志记录、事务管理、权限控制等。而 Spring AOP(Aspect-Oriented Programming,面向切面编程)提供了一种优雅的方式,让我们可以在不修改业务代码的情况下增强方法行为。
许多初学者会疑惑,OOP(面向对象编程)已经很好地组织了代码,为什么还需要 AOP(面向切面编程)?
OOP(面向对象编程)
UserService
处理用户业务逻辑。** AOP(面向切面编程)**
举例:
假设你要给所有 service
方法添加日志,如果用 OOP,你可能需要在每个方法里手写 System.out.println("方法开始执行");
,这会导致代码冗余。
而使用 AOP,你只需要定义一个 @Aspect
,就可以统一管理所有日志逻辑!
@Aspect
和 @After
等 AOP 注解的作用是什么?Spring AOP 通过 切面(Aspect) 来增强方法行为,主要依赖以下核心注解:
注解 | 作用 | 适用位置 |
---|---|---|
@Aspect |
标记一个切面类 | 类 |
@Component |
让 Spring 管理这个切面 | 类 |
@Before |
在目标方法执行前增强 | 方法 |
@After |
在目标方法执行后增强(无论是否抛异常) | 方法 |
@AfterReturning |
在目标方法成功返回后增强 | 方法 |
@AfterThrowing |
在目标方法抛出异常后增强 | 方法 |
@Around |
在方法执行前后都可以增强(可以控制方法是否执行) | 方法 |
** 示例:日志切面**
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.UserService.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("方法开始:" + joinPoint.getSignature().getName());
}
@After("execution(* com.example.service.UserService.*(..))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("方法结束:" + joinPoint.getSignature().getName());
}
}
** 作用:**
@Before
:在 UserService
的方法执行前打印日志。@After
:在方法执行后(无论是否异常)打印日志。execution(* com.example.service.*.*(..))
语法解析Spring AOP 通过切点表达式(Pointcut Expression)来指定哪些方法需要被增强,execution(...)
是其中最常用的一种写法。
表达式 | 作用 |
---|---|
execution(* com.example.service.UserService.*(..)) |
拦截 UserService 类的所有方法 |
execution(public * com.example.service.*.*(..)) |
拦截 service 包下所有 public 方法 |
execution(* com.example.service..*.*(..)) |
拦截 service 及其子包下的所有方法 |
execution(* com.example.service.*.find*(..)) |
拦截 service 包下所有 find 开头的方法 |
** 例子:**
@Before("execution(* com.example.service.UserService.getUser(..))")
public void beforeGetUser() {
System.out.println("调用 getUser 方法");
}
** 只拦截 UserService.getUser()
,不会影响其他方法!**
service
包下的某个 Service 打印日志?如果你只想拦截 UserService
,不影响 OrderService
,可以这样做:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.UserService.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("【AOP 日志】方法执行前:" + joinPoint.getSignature().getName());
}
@After("execution(* com.example.service.UserService.*(..))")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("【AOP 日志】方法执行后:" + joinPoint.getSignature().getName());
}
}
** 这样只有 UserService
的方法才会触发日志,而 OrderService
不受影响!**
如果你希望同时拦截 UserService
和 OrderService
,可以这样写:
@Before("execution(* com.example.service.UserService.*(..)) || execution(* com.example.service.OrderService.*(..))")
或者,如果你的 service
类名都是 xxxService
,可以这样写:
@Before("execution(* com.example.service.*Service.*(..))")
这样所有 UserService
、OrderService
、PaymentService
等符合 *Service
规则的类都会被拦截!
问题 | 核心解决方案 |
---|---|
AOP 和 OOP 的区别? | AOP 关注横切关注点,解耦日志、事务等逻辑 |
@Aspect 、@After 等注解的作用? |
定义切面,增强方法 |
execution(* com.example.service.*.*(..)) 是什么? |
切点表达式,匹配 service 包的所有方法 |
只对 UserService 打印日志? |
execution(* com.example.service.UserService.*(..)) |
如何拦截多个 Service? | execution(* com.example.service.*Service.*(..)) |
Spring AOP 让日志、权限、事务等逻辑和业务代码解耦,提高代码可维护性!
掌握 execution(...)
语法,可以精准控制拦截哪些方法!
如果你有其他 AOP 相关的疑问,欢迎在评论区交流!