Contents

AspectJ 中的 Joinpoint 与 ProceedingJoinPoint

1. 简介

在这个简短的教程中,我们将了解*AspectJ JoinPointProceedingJoinPoint*接口之间的区别。

我们将通过简短的解释和代码示例对其进行介绍。

2. JoinPoint

JoinPoint是一个AspectJ接口,它提供对给定连接点可用状态的反射访问,例如方法参数、返回值或抛出的异常。它还提供有关方法本身的所有静态信息。

我们可以将它与*@Before*、@After@AfterThrowing和*@AfterReturning*建议一起使用。这些切入点将分别在方法执行前、执行后、返回值后、或仅在抛出异常后、或仅在方法返回值后启动。

为了更好地理解,让我们看一个基本的例子。首先,我们需要声明一个切入点。我们将定义为ArticleService类中*getArticleList()*的每次执行:

@Pointcut("execution(* com.blogdemo.ArticleService.getArticleList(..))")
public void articleListPointcut(){ }

接下来,我们可以定义通知。在我们的示例中,我们将使用*@Before*:

@Before("articleListPointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
    log.info(
      "Method {} executed with {} arguments",
      joinPoint.getStaticPart().getSignature(),
      joinPoint.getArgs()
    );
}

在上面的示例中,我们使用*@Before*建议来记录方法执行及其参数。一个类似的用例是记录我们代码中发生的异常:

@AfterThrowing(
  pointcut = "articleListPointcut()",
  throwing = "e"
)
public void logExceptions(JoinPoint jp, Exception e) {
    log.error(e.getMessage(), e);
}

通过使用*@AfterThrowing*建议,我们确保仅在发生异常时才进行日志记录。

3. ProceedingJoinPoint

ProceedingJoinPointJoinPoint的扩展,它公开了附加的*proceed()*方法。调用时,代码执行会跳转到下一个通知或目标方法。它使我们能够控制代码流并决定是否继续进行进一步的调用。

可以只使用*@Around*建议,它围绕整个方法调用:

@Around("articleListPointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
    Object articles = cache.get(pjp.getArgs());
    if (articles == null) {
        articles = pjp.proceed(pjp.getArgs());
    }
    return articles;
}

在上面的示例中,我们说明了*@Around*建议最流行的用法之一。只有当缓存没有返回结果时,才会调用实际的方法。这正是Spring Cache Annotations 的工作方式。

我们还可以使用 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());
    }
}

例如,此解决方案可用于在网络中断的情况下重试 HTTP 调用。