前言:
从事 Java 的小伙伴都知道 Spring AOP,也都知道 AOP 是面向切面编程,那你知道 AOP 在项目实战中怎么使用吗?本篇简单分享 Spring AOP 在项目中的实际使用。
AOP 知识储备传送门:
AOP 基础知识回顾
AOP 概念
- 连接点(Join point):能够被拦截的地⽅、地点,Spring AOP 是基于动态代理的,所以是⽅法拦截的,每个成员⽅法都可以称之为连接点。
- 切点(Poincut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联,切点分为execution方式和annotation方式,前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
- 通知(Advice):在切面的某个特定的连接点上执行的动作,通知分为:前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能)。
- 织⼊(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入
- 引⼊/引介(Introduction):允许我们向现有的类添加新⽅法或属性,是⼀种特殊的增强。
- 切面(Aspect):切⾯由切点和增强、通知组成,它既包括了横切逻辑的定义、也包括了连接点的定义。
通知(Advice)分类
- 前置通知(Before Advice):在⽬标⽅法被调⽤前的通知功能。
- 后置通知(After Advice):在⽬标⽅法被调⽤之后的通知功能。
- 返回通知(After-returning):在⽬标⽅法成功执⾏之后的通知功能。
- 异常通知(After-throwing):在⽬标⽅法抛出异常之后的通知功能。
- 环绕通知(Around):把整个⽬标⽅法包裹起来,在被调⽤前和调⽤之后的通知功能。
自定义 AOP 切面常用知识储备
@Aspect 注解的作用?
@Aspect 注解的作用是定义一个切面,需要再自定义切面类上加上次注解。
@Component 注解
@Component 注解表示这个类交给 Spring 容器管理,如果不使用 @Component 注解,也要以其他方法是注册切面类,以确保 Spring 容器能够识别并管理这个切面。
Pointcut 切点中 execution 和 @annotation 定义切面的区别?
-
@annotation:切点表达式是注解的全限类名,是根据注解来匹配的,有注解的方法才会被拦截。
-
execution:切点表达式很灵活,有修饰符匹配、返回值匹配、类路径匹配、方法名匹配、参数匹配、异常类型匹配。
-
作用于任意以 public 修饰的方法:execution(public * *(…))
-
作用于任何一个以 set 开始的方法:execution(* set*(…))
-
作用于 MyService 接口的任意方法:execution(* com.xxx.service.MyService.*
-
作用于定义在 service 包和所有子包里的任意类的任意方法:execution(* com.xxx.service….(…)),第一个 * 表示匹配任意的方法返回值, …(两个点)表示零个或多个,第一个 … 表示 service 包及其子包,第二个 * 表示所有类,第三个 * 表示所有方法,第二个…表示方法的任意参数个数。
JoinPoint 的作用?
PoinPoint 代表程序中的一个点,通常把 JoinPoint 作为一个参数传递到通知方法中,通过 JoinPoint 来获取程序中这个点的相关信息,JoinPoint 只能用于 @Before、@After、@AfterReturning、@AfterThrowing 通知中,不能使用于 @Around 中,JoinPoint 常用方法如下:
- Object[] getArgs():返回目标方法的参数。
- Signature getSignature():返回方法的签名。
- Object getTarget():返回被增强的目标对象,也就是被代理对象。
- Object getThis():返回由目标对象生成的代理对象。
ProceedingJoinPoint 的作用?
ProceedingJoinPoint 是 JoinPoint 的子接口,用于环绕通知 @Around 中,它提供了一个 proceed() 方法,用于执行被拦截的方法也就是目标方法。
自定义 AOP 切面代码演示
自定义 AOP 切面实现接口请求日志记录
记录接口请求日志这种业务场景是非常常见的,如果在每个接口中去记录,显得代码臃肿且不灵活,而且有很大的侵入性,我们可以使用自定义 AOP 面来完成这种功能,代码如下:
@Slf4j
@Component
@Aspect
public class MyApiLogAspect implements Ordered {
@Pointcut("execution(* com.my.study.controller..*(..))")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis()