什么是 AOP

  • AOP: Aspect Oriented Programming (面向切面编程),是一种编程范式,侧重于将横切关注点(如日志记录、事务管理等)与核心业务逻辑分离,实现解耦和代码复用。

  • 实现方式

    • 动态代理:AOP 的核心实现方式之一。Spring AOP 是 Spring 框架的高级技术,基于动态代理机制,在运行时对特定的方法进行编程和增强。

案例: 统计各个业务层方法执行耗时

添加 AOP 依赖

pom.xml 中导入 Spring AOP 的依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写 AOP 切面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
@Aspect
public class TimeAspect {

@Around("execution(* com.itheima.service.*.*(..))") // 切入点表达式
public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long begin = System.currentTimeMillis();

// 执行目标方法
Object object = proceedingJoinPoint.proceed();

long end = System.currentTimeMillis();
log.info("{} 执行耗时: {} ms", proceedingJoinPoint.getSignature(), end - begin);

return object;
}
}
  • 切入点@Around("execution(* com.itheima.service.*.*(..))") 匹配 com.itheima.service 包下的所有方法。
  • 通知recordTime 方法是通知,在目标方法前后执行,并记录执行时间。

AOP 执行场景

  • 记录操作日志:例如记录每次服务方法调用时的日志。
  • 权限控制:对服务方法进行权限检查,决定是否允许访问。
  • 事务管理:在方法执行过程中自动管理事务。

AOP 的优势

  1. 代码无侵入:无需修改原有业务代码,即可完成增强。
  2. 减少重复代码:横切关注点(如日志、权限等)可以通过 AOP 集中管理,减少重复代码。
  3. 提高开发效率:通过分离关注点,简化核心业务代码。
  4. 维护便捷:横切关注点的修改只需在 AOP 切面中完成,维护简单。

AOP 核心概念

  1. 连接点 (JoinPoint):可以被 AOP 增强的方法。Spring AOP 的连接点主要指方法的执行。
  2. 通知 (Advice):定义了在连接点上执行的增强逻辑,如日志记录或事务管理。常见的通知类型包括前置通知、后置通知、环绕通知等。
  3. 切入点 (PointCut):定义了在哪些连接点上应用通知,即匹配哪些方法。
  4. 切面 (Aspect):通知和切入点的组合。切面类定义了增强的逻辑和应用范围。
  5. 目标对象 (Target):被 AOP 增强的对象,原始业务对象。
  6. 代理对象 (Proxy):Spring AOP 在运行时生成的增强对象,目标对象的代理。

AOP 通知类型

  1. @Around:环绕通知,目标方法执行的前后都会被执行。
  2. @Before:前置通知,目标方法执行前被调用。
  3. @After:后置通知,目标方法执行后被调用,无论方法是否抛出异常。
  4. @AfterReturning:返回通知,目标方法正常返回后调用。
  5. @AfterThrowing:异常通知,目标方法抛出异常时调用。

注意@Around 通知需要显式调用 ProceedingJoinPoint.proceed() 来执行目标方法,其他通知则不需要。


AOP 切入点表达式

切入点表达式用于指定哪些方法会被增强,常见的表达式有:

  1. execution 表达式:根据方法的返回类型、类名、方法名、参数来匹配。

    • execution(返回类型 包名.类名.方法名(参数))
    • * 通配符可以匹配任意返回类型或参数,例如:
      1
      execution(* com.example.service.*.*(..))
  2. @annotation:匹配带有特定注解的方法。

    1
    @Before("@annotation(com.example.annotation.Loggable)")
  3. 逻辑操作符:可以使用 &&, ||, ! 来组合多个切入点表达式。

    1
    execution(* com.example..*.*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)

@PointCut 注解

@PointCut 注解用于将切点表达式抽取出来复用,便于管理和修改:

1
2
3
4
5
6
7
@Pointcut("execution(* com.example.service.*.*(..))")
private void serviceLayer() {}

@Around("serviceLayer()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 逻辑处理
}

通知执行顺序

当多个切面匹配到同一个方法时,通知的执行顺序可以通过类名或 @Order 注解控制:

  1. 默认顺序:按类名字母排序,字母靠前的切面类先执行前置通知,后执行后置通知。
  2. @Order 注解:可以使用 @Order 指定切面的优先级,数字越小优先级越高。

AOP 连接点 (JoinPoint)

  • 在 Spring AOP 中,JoinPoint 抽象了方法执行时的上下文信息。可以获取方法名、类名、参数等信息。
  • ProceedingJoinPointJoinPoint 的子接口,专门用于 @Around 通知中,允许控制方法的执行。