有这样一个需求,当调用某个方法抛出异常,比如通过 HttpClient 调用远程接口时由于网络原因报 TimeOut 异常;或者所请求的接口返回类似于“处理中”这样的信息,需要重复去查结果时,我们希望当前方法能够在这种特定的情况下,重复执行,如果达到了我们的期望,则不重复执行。而且,我们希望能够控制重试次数,不希望无限期执行下去。

Java 中有各种定时任务的实现,如 Spring 的 Schedule,Quartz 等,稍微想一下,显然不符合我们的需求。递归倒是可以,但是有些问题,先看下递归的实现:

private int retryTimes = 3;

@Test

public void upperMethod() {

method("123", "456");

}

public void method(String param1, String param2) {

System.out.println(param1 + param2);

// 其他一些操作,但是没有得到预期的返回结果,或者抛出异常

boolean isException = true;

if(isException && retryTimes > 0){

retryTimes --;

method(param1, param2);

}

}

method 方法是需要重复执行的,重复执行 3 次,加上第一次执行,一共 4 次。如果异常了,则在 catch 里面递归调用 method。如果返回“处理中”等情况,则进行判断,是否需要递归调用。

这里的问题是定义了 retryTimes 这样一个全局变量,不优雅,如果需要重复执行的方法较多,而且重复次数不一样,则需定义多个全局变量。递归可以优化一下:

@Test

public void upperMethod() {

method(3, "123", "456");

}

public void method(int retryTimes,String param1, String param2) {

System.out.println(param1 + param2);

// 其他一些操作,但是没有得到预期的返回结果,或者抛出异常

boolean isException = true;

if(isException && retryTimes > 0){

method(--retryTimes, param1, param2);

}

}

这里去掉了全局变量,但是 method 方法多了一个和自身逻辑无关的 retryTimes 变量,还不优雅。如果参数较多,还会显得混乱。

下面做了一个还算优雅的方法:

@Test

public void mainMethod() {

subMethod("123", "456");

}

public void subMethod(String param1, String param2) {

System.out.println(param1 + param2);

RetryUtil.setRetryTimes(3).retry(param1, param2);

}

增加了一个 RetryUtil 的工具类,设置重试次数,然后传入当前方法的参数,进行重复执行。这里的重点就是 RetryUtil 的实现:

public class RetryUtil {

private static ThreadLocal retryTimesInThread = new ThreadLocal<>();

/**

* 设置当前方法重试次数

*

* @param retryTimes

* @return

*/

public static RetryUtil setRetryTimes(Integer retryTimes) {

if (retryTimesInThread.get() == null)

retryTimesInThread.set(retryTimes);

return new RetryUtil();

}

/**

* 重试当前方法

*

按顺序传入调用者方法的所有参数

*

* @param args

* @return

*/

public Object retry(Object... args) {

try {

Integer retryTimes = retryTimesInThread.get();

if (retryTimes <= 0) {

retryTimesInThread.remove();

return null;

}

retryTimesInThread.set(--retryTimes);

String upperClassName = Thread.currentThread().getStackTrace()[2].getClassName();

String upperMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();

Class clazz = Class.forName(upperClassName);

Object targetObject = clazz.newInstance();

Method targetMethod = null;

for (Method method : clazz.getDeclaredMethods()) {

if (method.getName().equals(upperMethodName)) {

targetMethod = method;

break;

}

}

if (targetMethod == null)

return null;

targetMethod.setAccessible(true);

return targetMethod.invoke(targetObject, args);

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

}

为了防止多线程情况下出现并发问题,这里定义了一个 ThreadLocal 变量来存储当前线程的重试次数。然后通过 setRetryTimes ,一个静态方法来设置这个重试次数,并返回一个 RetryUtil 对象。

调用者通过返回的 RetryUtil 对象调用 retry 方法实现重试。retry 方法接收一个可变参数,因为调用者实际的参数不确定,这里要求按顺序传入调用者方法的所有参数。

接下来判断 ThreadLocal 变量是否小于等于 0 ,如果是,则说明重复次数已达到,返回 null;如果不是,则让 ThreadLocal 变量减一。接下来:

String upperClassName = Thread.currentThread().getStackTrace()[2].getClassName();

String upperMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();

来获取当前方法(retry)的上层方法名和上层类名。Thread.currentThread().getStackTrace() 得到线程的方法栈数组,数组的第二个元素 Thread.currentThread().getStackTrace() [1]  为当前方法栈,第三个元素 Thread.currentThread().getStackTrace() [2] 为上层方法栈,通过上层方法的栈帧得到上层方法的方法名和类名。

下面就是通过反射获取该类的所有方法,循环判断方法名是否等于所要重复执行的方法,如果是的话,执行该方法,参数就是传入可变参数。

可能大家会说反射会耗时,但我认为对于上述这种需求的情况,重试次数也不会太多,因此性能可以接受。

java retry 实现,java-retry实现相关推荐

  1. java retry(重试) spring retry, guava retrying 详解

    转载 自 http://blog.51cto.com/9250070/2156431 系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理 ...

  2. foxmail邮件加载失败重试_java retry(重试) spring retry, guava retrying 详解

    系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理:实现一个按条件,查询用户信息的服务. 小明:好的.没问题. 代码 UserServi ...

  3. bin/hive出错:Exception in thread main java.lang.RuntimeException: java.net.ConnectException: Call Fr

    安装好Hive后使用bin/hive命令启动hive,出现错误: Exception in thread "main" java.lang.RuntimeException: ja ...

  4. Exception in thread main java.lang.RuntimeException: java.net.ConnectException错误问题

    之前可以打开hive服务,后又出现错误: Logging initialized using configuration in jar:file:/opt/app/hive/lib/hive-comm ...

  5. java 读取txt,java读取大文件

    java 读取txt,java读取大文件 package com.bbcmart.util; import java.io.File; import java.io.RandomAccessFile; ...

  6. JAVA基础(JAVA 执行环境) 第一天

    JAVA程序有3中执行环境. (1)能够单独运行的程序,称为Java Application(Java应用程序). (2)在Internet浏览器中运行的程序,称为 Java Applet(JAVA小 ...

  7. java.utilDate和java.sql.Date

    java.utilDate和java.sql.Date由广州疯狂软件教育java培训分享: java程序中的时间类型有java.util.Date和java.sql.Date两种.两者之间的关系和转化 ...

  8. java 终极超类,Java问答:终极父类(3),java问答

    Java问答:终极父类(3),java问答Java问答:终极父类(上) Java问答:终极父类(下) Java问答:终极父类(2)-上篇 Java问答:终极父类(2)-下篇 在之前关于 Object ...

  9. java.lang.OutOfMemoryError: Java heap space错误及...

    为什么80%的码农都做不了架构师?>>>    以下是从网上找到的关于堆空间溢出的错误解决办法: java.lang.OutOfMemoryError: Java heap spac ...

  10. 浅谈Java SE、Java EE、Java ME三者的区别

    1. Java SE(Java Platform,Standard Edition).Java SE 以前称为 J2SE.它允许开发和部署在桌面.服务器.嵌入式环境和实时环境中使用的 Java 应用程 ...

最新文章

  1. c语言 字母 八进制表示'/1011',C语言C语言第一课:C语言概述为什么学习C语言怎样学习C语言.DOC...
  2. 华为存储iscsi配置_iscsi 华为存储配置 上课内容
  3. python面向对象设计模式_python面向对象之设计模式
  4. python 面向对象_Python新手入门【面向对象】
  5. PEEL!!!!!!!! it is the easiest way for English arguments
  6. 防止SQL SERVER的事件探查器跟踪软件
  7. x264编码参数大测试:03 subme与crf(c)
  8. 云+X案例展 | 电商零售类:云徙助力良品铺子「双11」
  9. python多变量拟合_Python曲线将多个参数拟合到多个数据集
  10. 美团笔试题——公司食堂
  11. USB设备无法识别的五大原因
  12. 北京市居民公共交通出行特征分析
  13. 第七届科技节电子设计大赛须知
  14. 最全前端面试问题及答案总结
  15. Eplan-中断点(3)
  16. 「 最新大厂常考架构技术面试题 + 详细解答」
  17. Cool Edit之生成.pk文件问题
  18. Recall, Precision, and Average Precision
  19. Android 虚拟按键隐藏或显示之后共享元素动画异常解决方案
  20. 超平面、Wx=b或Wx+b=0的几何意义

热门文章

  1. 【script】python中的函数式编程
  2. Java GridBagLayout简单电子邮件发送界面的实现
  3. 能用计算机解决的问题十个,计算机常见问题及解决方法,计算机十项常见故障...
  4. oracle 数据树,ORACLE 树形数据
  5. java 微信图片上传_微信小程序图片上传java端以及前端实现
  6. spring boot入门小案例
  7. zookeeper 负载均衡 概念笔记
  8. OGNL、EL表达式——Struts
  9. cogs 167. [USACO Mar07] 月度花费
  10. javascipt很有用的代码,实现全选与反选,还可以与struts2或sevelet交互使用