1. Introduction
In this short tutorial, we'll learn about the differences between JoinPoint and ProceedingJoinPoint interfaces in AspectJ.
We'll cover it with a brief explanation and code examples.
2. JoinPoint
JoinPoint is an AspectJ interface that provides reflective access to the state available at a given join point, like method parameters, return value, or thrown exception. It also provides all static information about the method itself.
We can use it with the @Before, @After, @AfterThrowing, and @AfterReturning advice. These pointcuts will launch respectively before the method execution, after execution, after returning a value, or only after throwing an exception, or only after the method returns a value.
For better understanding, let's take a look at a basic example. First, we'll need to declare a pointcut. We'll define as every execution of getArticleList() from ArticleService class:
@Pointcut("execution(* com.baeldung.ArticleService.getArticleList(..))")
public void articleListPointcut(){ }
Next, we can define the advice. In our example, we'll use the @Before:
@Before("articleListPointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
log.info(
"Method {} executed with {} arguments",
joinPoint.getStaticPart().getSignature(),
joinPoint.getArgs()
);
}
In the above example, we use the @Before advice to log method execution with its parameters. A similar use case would be to log exceptions that occur in our code:
@AfterThrowing(
pointcut = "articleListPointcut()",
throwing = "e"
)
public void logExceptions(JoinPoint jp, Exception e) {
log.error(e.getMessage(), e);
}
By using the @AfterThrowing advice, we make sure the logging happens only when the exception occurs.
3. ProceedingJoinPoint
ProceedingJoinPoint is an extension of the JoinPoint that exposes the additional proceed() method. When invoked, the code execution jumps to the next advice or to the target method. It gives us the power to control the code flow and decide whether to proceed or not with further invocations.
It can be just with the @Around advice, which surrounds the whole method invocation:
@Around("articleListPointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object articles = cache.get(pjp.getArgs());
if (articles == null) {
articles = pjp.proceed(pjp.getArgs());
}
return articles;
}
In the above example, we illustrate one of the most popular usages of @Around advice. The actual method gets invoked only if the cache doesn't return a result. It's the exact way the Spring Cache Annotations work.
We could also use the ProceedingJoinPoint and @Around advice to retry the operation in case of any exception:
@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());
}
}
This solution can be used, for example, to retry HTTP calls in cases of network interruptions.
4. Conclusion
In this article, we've learned about the differences between Joinpoint and ProceedingJoinPoint in AspectJ. As always, all the source code is available over on GitHub.