1. 引言

在这个简短教程中,我们将探讨AspectJ接口JoinPointProceedingJoinPoint之间的区别。我们将通过简洁的解释和代码示例来进行讲解。

2. JoinPoint

JoinPointAspectJ接口,它提供了对给定连接点(join point)状态的反射访问,如方法参数、返回值或抛出的异常。它还提供了关于方法的所有静态信息。

我们可以在@Before@After@AfterThrowing@AfterReturning建议中使用它。这些切点分别会在方法执行前、执行后、返回值后或仅在抛出异常后、或仅在方法返回值后触发。

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

@Pointcut("execution(* com.baeldung.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缓存注解的工作方式。

我们还可以使用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调用。

4. 总结

在这篇文章中,我们了解了JoinPointProceedingJoinPointAspectJ中的区别。如往常一样,所有源代码可在GitHub上找到。