长风吴彦祖 發表於 2026-1-13 08:57:40

Java编写自定义重试工具类的示例代码

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>1 重试工具类 RetryUtils源码</li><li>2 使用方式</li><ul class="second_class_ul"><li>示例1:任务无返回值</li><li>示例2:任务有返回值、需校验返回值</li><li>示例3:任务有返回值、无需校验返回值</li></ul></ul></div><p>Java重试工具类,零依赖。可配置项:接受的异常类型、返回值校验、最大重试次数、重试间隔时间。</p>
<p class="maodian"></p><h2>1 重试工具类 RetryUtils源码</h2>
<p>RetryUtils:</p>
<p>使用了lombok的@Slf4j注解用于打印日志,不用可移除。</p>
<div class="jb51code"><pre class="brush:java;">import com.example.exception.RetryException;

import lombok.extern.slf4j.Slf4j;

import java.util.function.Predicate;
import java.util.function.Supplier;

/**
* &lt;h2&gt;重试工具类&lt;/h2&gt;
*
* @author GFire
* @since 2025/4/22 17:58
*/
@Slf4j
public abstract class RetryUtils {
    /**
   * 失败重试
   *
   * @param task            执行的任务,无返回值
   * @param acceptException 可接受的异常类型,执行的任务抛出此异常(及其子类)则失败重试。null表示不接受任何异常
   * @param maxRetryCount   最大重试次数
   * @param waitTime      重试间隔等待时间, 单位毫秒, &lt;=0则不等待
   * @throws RetryException 如果任务重试超过最大次数、或抛出不可接受的异常,则统一包装抛出RetryException
   */
    public static void doWithRetry(Runnable task, Class&lt;? extends Throwable&gt; acceptException, int maxRetryCount, int waitTime) {
      if (task == null) {
            throw new IllegalArgumentException("task can not be null");
      }

      doWithRetry(() -&gt; {
            task.run();
            return 1;
      }, i -&gt; i == 1, acceptException, maxRetryCount, waitTime);
    }

    /**
   * 失败重试
   *
   * @param task            执行的任务,有返回值
   * @param isValid         判断任务返回值是否合法,不合法则失败重试
   * @param acceptException 可接受的异常类型,执行的任务抛出此异常(及其子类)则失败重试。null表示不接受任何异常
   * @param maxRetryCount   最大重试次数
   * @param waitTime      重试间隔等待时间, 单位毫秒, &lt;=0则不等待
   * @return supplier执行结果
   * @throws RetryException 如果任务重试超过最大次数、或抛出不可接受的异常,则统一包装抛出RetryException
   */
    public static &lt;T&gt; T doWithRetry(Supplier&lt;T&gt; task, Predicate&lt;T&gt; isValid, Class&lt;? extends Throwable&gt; 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 &lt;= 0) {
            throw new IllegalArgumentException("maxRetryCount must be &gt; 0");
      }

      T result = null;
      for (int tryCount = 1; tryCount &lt;= 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 &gt; 0 &amp;&amp; tryCount &lt; maxRetryCount) {
                sleep(waitTime); // 等待一段时间后重试
            }
      }
      throw new RetryException("result: " + result);
    }

    private static void handleException(Throwable e, Class&lt;? extends Throwable&gt; acceptException, int maxRetryCount, int tryCount) {
      log.error("error, tryCount: {}, Exception: ", tryCount, e);
      if (acceptException != null &amp;&amp; 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);
      }
    }
}
</pre></div>
<p>RetryException:</p>
<div class="jb51code"><pre class="brush:java;">/**
* &lt;h2&gt;重试异常&lt;/h2&gt;
*
* @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);
    }
}
</pre></div>
<p class="maodian"></p><h2>2 使用方式</h2>
<p class="maodian"></p><h3>示例1:任务无返回值</h3>
<div class="jb51code"><pre class="brush:java;"> // 模拟调用API接口,失败重试
RetryUtils.doWithRetry(() -&gt; apiService.query(), Exception.class, 3, 2000);
</pre></div>
<p>解释:任务<code>apiService.query()</code>无返回值、接受Exception异常、最大重试次数为3、重试间隔2秒</p>
<p class="maodian"></p><h3>示例2:任务有返回值、需校验返回值</h3>
<div class="jb51code"><pre class="brush:java;">/**
* 模拟发送通知
*/
public boolean send(String content) {
    try {
      RetryUtils.doWithRetry(() -&gt; 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;
}
</pre></div>
<p>解释:任务<code>noticeService.send(content)</code>有String类型的返回值、<code>isValid</code>方法判断返回值是否合法、接受Exception异常、最大重试次数为3、重试间隔2秒</p>
<p class="maodian"></p><h3>示例3:任务有返回值、无需校验返回值</h3>
<div class="jb51code"><pre class="brush:java;">RetryUtils.doWithRetry(() -&gt; noticeService.send(), (res) -&gt; true, Exception.class, 3, 2000);
</pre></div>
<p>解释:任务<code>noticeService.send()</code>有String类型的返回值、<code>(res) -&gt; true</code>认为任意返回值都合法(即无校验)、接受Exception异常、最大重试次数为3、重试间隔2秒</p>
<p>到此这篇关于Java编写自定义重试工具类的示例代码的文章就介绍到这了,更多相关Java自定义重试内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>聊聊Java中接口重试机制的几种解决方案</li><li>Java实现自定义重试工具类</li><li>Java中HTTP接口请求重试的实现方式</li><li>Java接口请求重试机制的几种常见方法</li><li>深入浅出Java中重试机制的多种方式</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Java编写自定义重试工具类的示例代码