@Component
@Aspect
public class ProjectAdvice{
@Pointcut("execution(* com.example.service.*Service.*(..))")
private void servicePt(){}
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint) throws Throwable{
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
long start = System.currentTimeMillis();
for(int i = 0; i < 10000; i++){
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("万次执行" + className + "." + methodName + "------" + (end-start) + "ms");
}
}
代码解析
1. Spring 组件和切面声明
@Component
@Aspect
public class ProjectAdvice {...}
通过@Component注解,我们告诉Spring框架,ProjectAdvice类是一个Spring组件,Spring容器将管理其生命周期。
@Aspect注解表明ProjectAdvice类是一个切面类,它可以包含切点(Pointcuts)和通知(Advices)。
2. 切点定义
@Pointcut("execution(* com.example.service.*Service.*(..))")
private void servicePt(){}
在这里,我们定义了一个切点servicePt,它匹配com.example.service包下所有以"Service"结尾的类的所有方法。
3. 环绕通知定义
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable{...}
我们定义了一个环绕通知runSpeed,它将围绕servicePt切点匹配的方法执行。这个通知接受一个ProceedingJoinPoint参数,它提供了对被通知方法的访问。
4. 通知方法体
在通知方法体中,我们做了以下几件事:
获取了被通知方法的签名。
从签名中提取了类名和方法名。
记录了方法执行前的时间。
在循环中执行了被通知方法10000次。
记录了方法执行后的时间。
计算并打印了执行10000次被通知方法所需的时间。
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
long start = System.currentTimeMillis();
for(int i = 0; i < 10000; i++){
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("万次执行" + className + "." + methodName + "------" + (end-start) + "ms");
结论
通过这个简单的示例,我们展示了如何使用Spring AOP框架定义切点和通知,以及如何通过环绕通知测量方法的执行时间。这只是Spring AOP功能的冰山一角,它提供了强大的功能来帮助开发者将横切关注点(cross-cutting concerns)与业务逻辑分离,提高代码的可维护性和可重用性。
ProceedingJoinPoint是Spring AOP(Aspect Oriented Programming, 面向切面编程)框架中的一个接口,它是JoinPoint接口的一个子接口。ProceedingJoinPoint仅用于围绕通知(Around Advice),并提供了一个方法proceed(),该方法用于明确控制被通知的方法的执行。下面是ProceedingJoinPoint的一些主要方面和用法:
1. proceed() 方法:
proceed()方法是ProceedingJoinPoint的核心,它允许你控制被通知方法的执行。你可以选择在什么时候调用proceed()方法,以及是否要调用它。例如,你可以在一些前置处理后调用proceed()方法,或者在一些条件满足时调用proceed()方法。
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
// 前置处理
...
Object result = pjp.proceed(); // 调用目标方法
// 后置处理
...
return result;
}
2. 获取方法签名和其他信息:
ProceedingJoinPoint提供了一些方法,使你能够获取有关正在执行的方法的信息。例如,你可以获取方法签名、方法名、参数等。
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature(); // 获取方法签名
String methodName = signature.getName(); // 获取方法名
Object[] args = pjp.getArgs(); // 获取方法参数
...
}
3. 修改方法参数:
虽然ProceedingJoinPoint不允许直接修改方法参数,但你可以通过proceed(Object[])方法传递新的参数给目标方法。
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs(); // 获取原始参数
// 修改参数
args[0] = ...;
Object result = pjp.proceed(args); // 使用新参数调用目标方法
...
}
通过ProceedingJoinPoint,你可以在围绕通知中拦截和控制目标方法的执行,这为在执行方法前后添加自定义逻辑提供了很大的灵活性。
getStaticPart() :
此方法返回 joinpoint 的静态部分。静态部分是一个可以安装一系列拦截器的可访问对象。
getThis() :
此方法返回持有当前 joinpoint 静态部分的对象。例如,对于一个调用,目标对象就是该方法返回的对象。如果可访问对象是静态的,那么此方法可能返回 null。
proceed() :
此方法用于进入拦截器链中的下一个拦截器。这个方法的实现和语义取决于实际的 joinpoint 类型(请参见子接口的定义)。如果 joinpoint 抛出异常,此方法将抛出 Throwable。
JoinPoint 为程序员提供了一种方法来解决交叉关注点(cross-cutting concerns),例如日志记录、事务管理、安全检查等,它通过识别程序中的特定点(例如方法调用或异常抛出)来实现这些功能。在这些点上应用切面(aspects)可以自动添加日志记录代码、执行特定操作,或在异常抛出时执行特定操作。
通过 JoinPoint,开发者可以获得连接点上下文的反射访问,包括被调用的方法的参数、返回值或抛出的异常,以及方法本身的所有静态信息。在 Spring AOP 中,JoinPoint 总是代表一个方法的执行,其信息可以通过在通知体中声明类型为 org.aspectj.lang.JoinPoint 的参数来获取。
这些方法和 JoinPoint 接口为 AOP 提供了强大的基础,使得开发者能够更容易地管理和组织代码,同时处理跨多个类型和对象的交叉关注点。
基本区别 :
ProceedingJoinPoint是JoinPoint的扩展,它提供了额外的proceed()方法。这个方法允许代码执行跳转到下一个通知或目标方法。通过proceed()方法,可以控制代码流程,并决定是否继续执行进一步的调用。
使用场景 :
JoinPoint通常用于@Before、@After、@AfterReturning和@AfterThrowing通知,这些通知分别在方法执行前、执行后、返回值后和抛出异常后触发。
ProceedingJoinPoint主要用于@Around通知,它允许在方法执行前后执行代码,并可以决定是否继续执行目标方法。
控制流程 :
JoinPoint提供对连接点上下文的访问,例如被调用的方法的参数、返回值或抛出的异常2。
ProceedingJoinPoint的proceed()方法提供了一种控制执行流程的方式,例如,可以更改方法的参数,或者在某些条件下决定是否调用目标方法。
方法控制 :
ProceedingJoinPoint的proceed()方法允许代码执行跳转到下一个通知或目标方法,它提供了一种控制代码流程的机制,比如可以决定是否继续进一步的调用。
使用ProceedingJoinPoint和@Around通知重试操作以应对任何异常的情况
@Around("articleListPointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
try {
return pjp.proceed(pjp.getArgs());
} catch (Throwable) {
log.error(e.getMessage(), e);
log.info("Retrying operation");
return pjp.proceed(pjp.getArgs());
}
}
在这个示例中,如果目标方法抛出异常,aroundAdvice方法会捕获异常并重试操作.
我在哪里