我们在工作中难免会写一些重复性的代码,所以需要我们具备一定的抽象能力,比如把共同的逻辑抽取到抽象类中,也可以通过一些工具类来避免冗余代码
今天这篇文章就是把一个调用服务的重试功能抽取出一个工具类,以备复用。这里为了方便介绍,把调用服务简化成方法的调用,被调用的 foo 方法如下:
public static List<String> foo() {// 没有显示抛出异常 System.out.println("调用方法"); // 模拟抛出异常 System.out.println(1/0); List<String> list = new ArrayList<>(); list.add("1"); return list; }
调用方和重试逻辑如下:
List<String> result = null; // 重试次数 int retryCount = 0; // 调用服务的开关,默认打开 boolean callSwitch = true; // 只要调用服务开关开着,并且重试次数不大于最大的重试次数,就调用服务 while (callSwitch && retryCount <= 3) { try { // 调用服务 result = foo(); // 省略了对结果的校验,假设到了这里就说明没有问题,把调用服务开关关掉 callSwitch = false; } catch (Exception e) { // 发生了异常(比如超时,就需要重试了) // 调用服务的开关打开 callSwitch = true; retryCount++; } } // 后面对 result 进行处理
其实上面的代码就已经解决了,服务重试的逻辑,测试没有问题后,可以提交代码让同事帮忙进行 CR 了,可是小朋同学看到这个代码后,给了建议:
可以抽象一层,提出一个 retry 的工具类,这样大家都可以简单复用你的 retry 逻辑
白牙心想,也对哈,那就提出一个工具类吧,可是发了会儿呆,竟然没有头绪(反映出了抽象能力的薄弱,平时要多注意抽象能力的培养)。小朋见状,给了一点提示,白牙立马在键盘上噼里啪啦敲击了起来,就有了下面的工具类
主要依赖函数式接口 Supplier 和 BiFunction
public class RetryUtil { public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) { T result = null; Exception exception = null; int retryCount = 0; boolean callMethod = true; while (callMethod && retryCount <= maxRetryCount) { try { // 获取调用服务的结果 result = supplier.get(); } catch (Exception e) { // 如果重试次数不小于最大重试次数,就抛出异常,我们把对异常的处理交给业务方 if (retryCount >= maxRetryCount) { throw e; } exception = e; } // 对结果进行判断 callMethod = consumer.apply(result, exception); if (callMethod) { retryCount++; } } return result; } }
业务调用方的代码如下:
List<String> result1 = retry(3,// 最大重试次数 ()-> foo(),// 调用服务 (list, e) -> e != null || list == null || list.isEmpty());// 对结果处理
自测没有问题后,又提交代码让小朋给 CR 一下,小朋凝视了会儿,就有了下面的对话
小朋:“retry 方法没有抛出异常”
白牙:“被调用的服务没有显示的抛出异常,这里也就没有抛出”
小朋:“那人如果有服务方显示抛出异常呢?”
白牙:“我再改一版”
服务方显示抛出了异常,这样 retry 方法也得显示抛出异常,但调用方就会显示会处理的异常,如下所示:
public static List<String> foo() throws Exception{// 显示抛出异常 System.out.println("调用方法"); // 模拟抛出异常 System.out.println(1/0); List<String> list = new ArrayList<>(); list.add("1"); return list; }
public class RetryUtil { public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{ // 省略... }
出现这种情况是因为 Supplier 的 get 方法没有抛出异常
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
既然你不支持,那就自己写个呗,于是就有了下面的 DspSupplier,它和 Supplier 的主要区别就是在 get 方法中显示抛出了异常
@FunctionalInterface interface DspSupplier<T> { /** * Gets a result. * * @return a result */ T get() throws Exception; }
于是 retry 方法就变成了下面这样子
public class RetryUtil { public static <T> T retry(int maxRetryCount, DspSupplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{ // 省略... }
使用了自定义的 Supplier 后,调用方就没有 “Unhandled exception” 了
上面的案例比较简单,但麻雀虽小,五脏俱全,也是一个不错的体验
欢迎关注公众号 【每天晒白牙】,获取最新文章,我们一起交流,共同进步!