AOP基础

SpringAOP实现步骤

导入依赖:在 pom.xml 文件中导入 AOP 的依赖

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

在类名上加@Aspect注解

在方法名上加具体AOP方法注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
@Aspect //当前类为切面类
@Slf4j
public class RecordTimeAspect {

@Around("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
//记录方法执行开始时间
long begin = System.currentTimeMillis();

//执行原始方法
Object result = pjp.proceed();

//记录方法执行结束时间
long end = System.currentTimeMillis();

//计算方法执行耗时
log.info("方法执行耗时: {}毫秒",end-begin);
return result;
}
}

核心概念:

  • 连接点:JoinPoint,可以被AOP控制的方法(类上加了@Aspect注解,里面的方法就都是)
  • 通知:Advice,指哪些重复的逻辑,也就是共性功能(方法体内容)
  • 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用 (也就是具体的AOP方法注解)
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点,即注解+方法)
  • 目标对象:Target,通知所应用的对象

AOP进阶

五种类型:

Spring AOP 通知类型
@Around 环绕通知,此注解标注的通知方法在目标方法前、后都被执行
@Before 前置通知,此注解标注的通知方法在目标方法前被执行
@After 后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
@AfterReturning 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
@AfterThrowing 异常后通知,此注解标注的通知方法发生异常后执行

抽取:

给一个函数加上@Pointcut注解,就可以在后面复用

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

//后面用的时候:
@Before("pt()")
@Around("pt()")

当切入点方法使用 private 修饰时,仅能在当前切面类中引用该表达式, 当外部其他切面类中也要引用当前类中的切入点表达式,就需要 public, 别的类用pt的时候,需要传递全类名+方法,如 com.dhrjava.aspect.myAspect.pt()

通知顺序

  • 目标方法前的通知方法:字母排名靠前的先执行
  • 目标方法后的通知方法:字母排名靠前的后执行

想改的话,使用Spring提供的 @Order 注解

切入点表达式

execution(……):根据方法的签名来匹配

1
execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)
  1. 其中带?的表示可以省略的部分
  • 访问修饰符:可省略(比如: public、protected)
  • 包名.类名: 可省略
  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
  1. 可以使用通配符描述切入点
  • * :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
  • .. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
  1. 根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式

@annotation(……) :根据注解匹配

  1. 自定义一个注解
  2. 在方法上加上这个注解
  3. 在切入点中找这个注解:@Before(“@annotation(com.itheima.anno.LogOperation)”)
  4. 依旧可以使用逻辑表达式