
AOP:就是面相切面编程,切面指的就是某一类特定的问题,也可以理解为面相特定方法编程,例如之前使用的拦截器,就是 AOP 思想的一种应用,统一数据返回格式和统一异常处理也是 AOP 思想的实现方式
比如说需要统计每个方法执行的耗时,如果正常来写的话,需要在方法的开头和结尾来定义时间戳相减

如果有很多方法都需要计算的话,总不能每个方法都写这些重复的代码吧,接下来看通过使用 AOP 思想是如何实现的
首先需要添加对应的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>然后需要添加@Component注解,

@Slf4j
@Aspect
@Component
public class TimeRecordAspect {
@Around("execution(* com.example.springbook.controller.*.*(..))")
public Object timeRecord(ProceedingJoinPoint pjt){
//记录开始时间
long start = System.currentTimeMillis();
//执行目标方法
Object o = null;
try {
o = pjt.proceed();
} catch (Throwable e){
throw new RuntimeException(e);
}
log.info(pjt.getSignature() + "执行耗时:" + (System.currentTimeMillis() - start));
return o;
}
}再去调用接口的话就会计算出来对应方法消耗的时间

来简单分析一下上面的代码:

Spring AOP 的通知类型有以下几种
・@Around:环绕通知,在目标方法前、后都被执行。 ・@Before:前置通知,在目标方法前被执行。 ・@After:后置通知,在目标方法后被执行,无论是否有异常都会执行。 ・@AfterReturning:返回后通知,在目标方法后被执行,有异常不会执行。 ・@AfterThrowing:异常后通知,发生异常后执行。
接下来同时测试一下这些通知类型,

来看一下接口正常返回的情况下的执行顺序

再来看接口发生异常的情况下的执行顺序:

从上面的结果上就可以看出,Around 可以完成其他类型的功能
需要注意的是:

在上面的代码中还存在一个问题,每次写一个方法都需要写一个切点表达式,如果说更换切点的话,那么所有的切点表达式都要修改一下,就可以通过@Pointcut 注解,把公共的切点表达式提取出来,需要用到时引用该切点表达式即可

这样提取出来,其他方法想要调用直接写上方法名称即可,和定义的常量类似,那么同一个类下可以直接调用,如果是不同的类的话需要把全限定名写上,并写明是 xx 类的 xx 方法
@Around("com.example.springaop.aspect.AspectDemo.pt()")
执行之后也是生效了

但是如果定义时设置为了 private 的话其他类就不能执行了
@Pointcut("execution(* com.example.springaop.controller.*.*(..))")
private void pt(){
}当在一个项目中定义了多个切面类时,并且这些切面类的多个切入点都匹配到了同一个目标方法,那么目标方法执行的时候,这些切面类中的通知方法都会执行,那么这时就会有一个优先级,哪个切面类先执行

通过测试发现,执行的顺序也是类似于一个切面的

关于切面类的执行顺序,默认是按照类名的字典序来执行的

可以@Order注解通过来修改优先级

这样 AspectDemo2 的优先级就变为最高的了,就先执行,也就是数字越大优先级越高

访问修饰符和异常可以省略
来看具体示例:








使用 execution 表达式匹配的方法都是具有一定规律的,比如 xx 包的 xx 类的 xx 方法,那么如果没有规律可循的话就需要使用 @annotation 注解了
首先,可以通过自定义注解的方式,自定义注解的创建需要选择 @Annotation

@Retention(RetentionPolicy.RUNTIME) //注解的有效阶段
@Target({ElementType.METHOD}) //表示方法注解
public @interface TimeRecord {
}然后在原来计时的方法上来使用 @annotation 来指明要使用的注解

接下来只要是添加了自定义的注解都会执行这里的方法

通过这种方式就实现了想要给哪个方法生效就直接加上注解就可以了
除了自定义注解,其他现存的注解也是可以这样使用的

例如,可以把@RequestMapping的路径写在@annotation里,就表示只要加了@RequestMapping的方法都可以生效
