AOP 使用
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
AOP 实现
java
@Aspect
@Component
public class RecordTimeAspect {
// 我要代理的路径类中的方法
@Around("execution(* com.example.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("记录时间");
long start = System.currentTimeMillis();
// 目标方法执行
Object proceed = pjp.proceed();
// 获取方法签名
pjp.getSignature();
long end = System.currentTimeMillis();
System.out.println("方法执行耗时:" + (end - start));
return proceed;
}
}
AOP 的原理
- 连接点:表示JoinPoint 被AOP控制的方法,包含方法信息
- 通知:Advice 表示要升级的功能,也就是AOP中升级的方法
- 切入点:PointCut 表示要匹配哪些条件
- 切面:Aspect,通知+切入点
- 目标对象:Target 通知所应用的对象,也就是原始类
用Service 打印命名,可以看出是代理对象还是原类。
java
helloService.getClass().getName() // 下面输出表示是代理的。
// com.example.service.HelloService$$EnhancerBySpringCGLIB$$2a189639
通知
根据通知方法执行时机的不同,将通知类型分为以下常见的五类:
- @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
- @Before:前置通知,此注解标注的通知方法在目标方法前被执行
- @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning: 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
@Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值。
Java
@Aspect
@Component
public class RecordTimeAspect {
// 定义切点
@Pointcut("execution(* com.example.service.*.*(..))")
public void pt() {
}
@Before("pt()")
public void before(JoinPoint joinPoint) {
System.out.println("开始执行方法:" + joinPoint.getSignature().getName());
}
@Around("pt()")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("记录时间");
long start = System.currentTimeMillis();
// 目标方法执行
Object proceed = pjp.proceed();
// 获取方法签名
pjp.getSignature();
long end = System.currentTimeMillis();
System.out.println("方法执行耗时:" + (end - start));
return proceed;
}
@After("pt()")
public void after(JoinPoint joinPoint) {
System.out.println("结束执行方法:" + joinPoint.getSignature().getName());
}
@AfterReturning("pt()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("方法正常返回,返回结果为:" + joinPoint.getSignature().getName());
}
@AfterThrowing("pt()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("方法异常返回,异常结果为:" + joinPoint.getSignature().getName());
}
}
通知顺序
如果一个方法被多个切面,切到了是都会执行的。
默认顺序按照类名的字母排序。
用 @Order(数字) 加在切面类上来控制顺序
- 目标方法前的通知方法:数字小的先执行
- 目标方法后的通知方法:数字小的后执行
大概流程是,环绕->方法运行之前,如果有多个切面,第二个切面执行,环绕->方法运行之前->方法运行结束或者方法报错 ->回到环绕...如此执行。
java
@Aspect
@Component
@Order(3)
public class RecordTimeAspect {
切入点表达式
- 介绍:描述切入点方法的一种表达式。
- 作用:用来决定项目中的哪些方法需要加入通知
常见形式:
- execution(......):根据方法的签名来匹配
- @annotation(......) :根据注解匹配


注解方式
java
@Before("@annotation(com.ruoyi.common.annotation.DataScope)")
public void doBefore(JoinPoint point) {
}
注解的第二种方式,可以直接获取注解
java
@DataScope(deptAlias = "dddd")
public String hello() {
// 切面类中
@DataScope(deptAlias = "dddd")
public String hello() {
JoinPoint 类
java
@Before("@annotation(dataScope)")
public void before(JoinPoint joinPoint, DataScope dataScope) {
// 获取方法名
String name = joinPoint.getSignature().getName();
// 获取目标对象
Object target = joinPoint.getTarget();
// 获取目标类
String name1 = target.getClass().getName();
// 获取目标方法参数
Object[] args = joinPoint.getArgs();
}
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。
- 对于 @Around 通知,获取连接点信息只能使用 ProceedingJoinPoint
- 对于其它四种通知,获取连接点信息只能使用 JoinPoint ,它是 ProceedingJoinPoint 的父类型