Java重试工具类,零依赖。可配置项:接受的异常类型、返回值校验、最大重试次数、重试间隔时间。
1 重试工具类 RetryUtils源码
RetryUtils:
使用了lombok的@Slf4j注解用于打印日志,不用可移除。
java
import com.example.exception.RetryException;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* <h2>重试工具类</h2>
*
* @author GFire
* @since 2025/4/22 17:58
*/
@Slf4j
public abstract class RetryUtils {
/**
* 失败重试
*
* @param task 执行的任务,无返回值
* @param acceptException 可接受的异常类型,执行的任务抛出此异常(及其子类)则失败重试。null表示不接受任何异常
* @param maxRetryCount 最大重试次数
* @param waitTime 重试间隔等待时间, 单位毫秒, <=0则不等待
* @throws RetryException 如果任务重试超过最大次数、或抛出不可接受的异常,则统一包装抛出RetryException
*/
public static void doWithRetry(Runnable task, Class<? extends Throwable> acceptException, int maxRetryCount, int waitTime) {
if (task == null) {
throw new IllegalArgumentException("task can not be null");
}
doWithRetry(() -> {
task.run();
return 1;
}, i -> i == 1, acceptException, maxRetryCount, waitTime);
}
/**
* 失败重试
*
* @param task 执行的任务,有返回值
* @param isValid 判断任务返回值是否合法,不合法则失败重试
* @param acceptException 可接受的异常类型,执行的任务抛出此异常(及其子类)则失败重试。null表示不接受任何异常
* @param maxRetryCount 最大重试次数
* @param waitTime 重试间隔等待时间, 单位毫秒, <=0则不等待
* @return supplier执行结果
* @throws RetryException 如果任务重试超过最大次数、或抛出不可接受的异常,则统一包装抛出RetryException
*/
public static <T> T doWithRetry(Supplier<T> task, Predicate<T> isValid, Class<? extends Throwable> acceptException, int maxRetryCount, int waitTime) {
if (task == null) {
throw new IllegalArgumentException("task can not be null");
}
if (isValid == null) {
throw new IllegalArgumentException("isValid can not be null");
}
if (maxRetryCount <= 0) {
throw new IllegalArgumentException("maxRetryCount must be > 0");
}
T result = null;
for (int tryCount = 1; tryCount <= maxRetryCount; tryCount++) {
try {
result = task.get();
if (isValid.test(result)) {
return result;
} else {
log.error("result invalid, tryCount: {}, result: {}", tryCount, result);
}
} catch (Throwable e) {
handleException(e, acceptException, maxRetryCount, tryCount);
}
if (waitTime > 0 && tryCount < maxRetryCount) {
sleep(waitTime); // 等待一段时间后重试
}
}
throw new RetryException("result: " + result);
}
private static void handleException(Throwable e, Class<? extends Throwable> acceptException, int maxRetryCount, int tryCount) {
log.error("error, tryCount: {}, Exception: ", tryCount, e);
if (acceptException != null && acceptException.isInstance(e)) {
if (tryCount == maxRetryCount) { // 最后一次重试仍失败,则抛出
throw new RetryException(e);
}
} else { // 不可接受的异常,直接抛出
throw new RetryException(e);
}
}
private static void sleep(int waitTime) {
try {
Thread.sleep(waitTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RetryException(e);
}
}
}
RetryException:
java
/**
* <h2>重试异常</h2>
*
* @author GFire
* @since 2025/4/23 11:20
*/
public class RetryException extends RuntimeException {
public RetryException(String message) {
super(message);
}
public RetryException(Throwable cause) {
super(cause);
}
}
2 使用方式
示例1:任务无返回值
java
// 模拟调用API接口,失败重试
RetryUtils.doWithRetry(() -> apiService.query(), Exception.class, 3, 2000);
解释:任务apiService.query()无返回值、接受Exception异常、最大重试次数为3、重试间隔2秒
示例2:任务有返回值、需校验返回值
java
/**
* 模拟发送通知
*/
public boolean send(String content) {
try {
RetryUtils.doWithRetry(() -> noticeService.send(content), this::isValid, Exception.class, 3, 2000);
return true;
} catch (RetryException e) {
log.error("send error: {}", e.toString());
}
return false;
}
private boolean isValid(String res) {
if (StringUtils.isNotEmpty(res)) {
JSONObject response = JSON.parseObject(res);
return "ok".equals(response.getString("status"));
}
return false;
}
解释:任务noticeService.send(content)有String类型的返回值、isValid方法判断返回值是否合法、接受Exception异常、最大重试次数为3、重试间隔2秒
示例3:任务有返回值、无需校验返回值
java
RetryUtils.doWithRetry(() -> noticeService.send(), (res) -> true, Exception.class, 3, 2000);
解释:任务noticeService.send()有String类型的返回值、(res) -> true认为任意返回值都合法(即无校验)、接受Exception异常、最大重试次数为3、重试间隔2秒