I faced similar issues when dealing with calling multiple apis from rest service, where i was suppose to provide a fallback implementation when error occured. My Aspect
was more than what i am giving example here.
Service
@Service
public class SuspiciousService {
final Random random = new Random();
public String beSuspicious() {
final boolean value = random.nextBoolean();
if (value) {
throw new IllegalStateException("Exception occured for: " + value);
}
return "I am not suspicious";
}
}
Sample service which randomly throws an error.
Controller
@RestController
@RequestMapping("/is-suspicious")
@AllArgsConstructor
public class SampleController {
private final SuspiciousService suspiciousService;
@GetMapping
public Map<String, String> get() {
return Map.of("isSuspicious", suspiciousService.beSuspicious());
}
}
Controller which invokes this service.
Finally, Around Aspect
catches this exception and provides the sample response.
@Aspect
@Component
@Order(2)
public class AspectAroundSuspiciousService {
@Around("execution(* in.silentsudo.sprintbootresttemplate.SuspiciousService.beSuspicious(..))")
public Object parallelExecuteBeforeAndAfterCompose(ProceedingJoinPoint point) throws Throwable {
try {
return point.proceed();
} catch (RuntimeException re) {
return "Yes, I am suspicious";
}
}
}
The other approach is if you are using ByteBuddy, you can add annotation to the method throwing exception
@Advice.OnMethodExit(onThrowable = RuntimeException.class)
and have an ExceptionHandler to cath this
@ExceptionHandler
private String suspiciousRuntimeException(RuntimeException exception) {
return "Yes, I am suspicious from ex handler, error: " + exception.getMessage();
}
I choose aspect over bytebuddy for simple reason as i was handling ladder of api exception, where as this implementation will catch in general RuntimeException happenning from service#method