在说到Spring框架,我们往往对Spring MVC
,Spring Web
更加熟悉。
Spring AOP
也是Spring
大家族中的一员。相比于机械的记忆,在实际做项目的过程中,我才真正的接触和应用到它,也理解了它的含义。
首先提到Spring AOP
,就不得不说到AOP
:面向切面编程。
关于它的定义有很多,这里无需赘述,总之在实际应用中,我们往往需要自己定义注解,编写切面类,比如下面的注解,我们要重写日志,以便于在控制台看到足够的信息。
自定义注解
java">package com.quanxiaoha.weblog.common.aspect;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ApiOperationLog {/*** API 功能描述** @return*/String description() default "";}
在这个注解里,设置了一个默认的description为""
注解有了,我们需要编写切面类:
java">@Aspect
@Component
@Slf4j
public class ApiOperationLogAspect {/** 以自定义 @ApiOperationLog 注解为切点,凡是添加 @ApiOperationLog 的方法,都会执行环绕中的代码 */@Pointcut("@annotation(com.quanxiaoha.weblog.common.aspect.ApiOperationLog)")public void apiOperationLog() {}/*** 环绕* @param joinPoint* @return* @throws Throwable*/@Around("apiOperationLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {try {... ...(省略)// 获取被请求的类和方法String className = joinPoint.getTarget().getClass().getSimpleName();String methodName = joinPoint.getSignature().getName();... ...// 执行切点方法Object result = joinPoint.proceed();... ...return result;} finally {MDC.clear();}}
}
这个切面类仅作示例,且源代码为我跟做的项目的源码,因此这里隐藏了部分,仅留下一些框架作为讲解。
由于这个是环绕型的,所以我们在编写的时候需要选用ProceedingJoinPoint
而不是JoinPoint
具体原因可以看方法体里的代码,首先要获取被请求的类和方法,然后执行一系列操作后还要继续执行注解方法joinPoint.proceed()
,然后再进行一系列操作。
由此,我们能够看出它的特点,就是它可以控制目标方法继续执行。
区别
ProceedingJoinPoint
和 JoinPoint
都是 Spring AOP
中的接口,用于在切面编程中获取方法的相关信息。它们的区别主要在于功能上,尤其是在环绕通知(@Around
)和其他通知(如前置通知、后置通知)中的使用方式。
JoinPoint 接口:
JoinPoint
是 Spring AOP
中最基础的接口,它代表了目标方法执行的一个“连接点”。JoinPoint
提供了对方法签名、参数等基本信息的访问。
主要功能:
- 获取目标方法的 签名(getSignature());
- 获取目标方法的 参数(getArgs());
- 获取目标方法的 目标类(getTarget());
- 获取当前连接点的 方法名(getSignature().getName())。
使用场景:
JoinPoint
一般用于 前置通知、后置通知 或 异常通知 等。这些通知不需要控制目标方法的执行流程。
示例:
java">@Around("execution(* com.example.service.*.*(..))")
public Object logAround(JoinPoint joinPoint) {// 获取方法名String methodName = joinPoint.getSignature().getName();// 获取参数Object[] args = joinPoint.getArgs();// 打印日志System.out.println("Method name: " + methodName);System.out.println("Arguments: " + Arrays.toString(args));return null;
}
ProceedingJoinPoint 接口:
ProceedingJoinPoint
是 JoinPoint
的子接口,提供了比 JoinPoint
更多的功能,特别是在环绕通知中,用于继续执行目标方法。
主要区别:
继续执行目标方法:ProceedingJoinPoint
提供了一个proceed()
方法,该方法用于在环绕通知中继续执行目标方法,并且可以传递新的参数。如果你使用的是 JoinPoint
,就无法在通知中继续执行目标方法。
与 JoinPoint
相同的功能:ProceedingJoinPoint
继承了 JoinPoint
的所有基本功能,例如获取方法签名、方法参数等。
使用场景:
ProceedingJoinPoint
主要用于 环绕通知,因为只有环绕通知需要对目标方法的执行进行控制。
示例:
java">@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {// 获取方法名String methodName = proceedingJoinPoint.getSignature().getName();// 获取参数Object[] args = proceedingJoinPoint.getArgs();// 打印日志System.out.println("Method name: " + methodName);System.out.println("Arguments: " + Arrays.toString(args));// 执行目标方法Object result = proceedingJoinPoint.proceed();// 执行完毕后打印返回值System.out.println("Result: " + result);return result;
}
主要区别总结:
特性 | JoinPoint | ProceedingJoinPoint |
---|---|---|
功能 | 获取方法签名、参数等基本信息 | 获取方法签名、参数等基本信息,并且能够控制目标方法的执行 |
通知类型 | 用于前置通知、后置通知、异常通知等 | 仅用于环绕通知 |
是否可以控制目标方法执行 | 不可以 | 可以,通过 proceed() 方法继续执行目标方法 |