As stated in the Javadoc for RetryListener.onRetryPolicyExhaustion(), the supplied Throwable should be:

the last exception thrown by the Retryable operation

However, RetryTemplate.execute() currently supplies the final, composite RetryException to RetryListener.onRetryPolicyExhaustion(), whose cause happens to be the last exception thrown by the Retryable operation.