Java 中实现方法重试的一种机制
有这样一个需求,当调用某个方法抛出异常,比如通过 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<Integer> retryTimesInThread = new ThreadLocal<>();
/**
* 设置当前方法重试次数
*
* @param retryTimes
* @return
*/
public static RetryUtil setRetryTimes(Integer retryTimes) {
if (retryTimesInThread.get() == null)
retryTimesInThread.set(retryTimes);
return new RetryUtil();
}
/**
* 重试当前方法
* <p>按顺序传入调用者方法的所有参数</p>
*
* @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 中实现方法重试的一种机制的更多相关文章
- Java中获取键盘输入值的三种方法
Java中获取键盘输入值的三种方法 Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值 ...
- Java中从控制台输入数据的几种常用方法
Java中从控制台输入数据的几种常用方法 一.使用标准输入串System.in //System.in.read()一次只读入一个字节数据,而我们通常要取得一个字符串或一组数字 //System.in ...
- c#和java中的方法覆盖——virtual、override、new
多态和覆盖 多态是面向对象编程中最为重要的概念之一,而覆盖又是体现多态最重要的方面.对于像c#和java这样的面向对象编程的语言来说,实现了在编译时只检查接口是否具备,而不需关心最终的实现,即最终的实 ...
- Java中的方法(形参及实参)return返回类型
如何定义 Java 中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 一般情况下,定义一个方法的语法是: 其中: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 pub ...
- JAVA中native方法调用
在Java中native是关键字.它一般在本地声明,异地用C和C++来实现.它的声明有几点要注意:1)native与访问控制符前后的关系不受限制.2)必须在返回类型之前.3)它一般为非抽象类方法.4) ...
- Java学习笔记十一:Java中的方法
Java中的方法 一:什么是方法: 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 学过C语言或者其他语言的应该都知道函数这个东西,在Java中,其实方法就是函数,只不过叫法不同,在 ...
- java中equals方法和==的用法
java中equals方法的用法以及==的用法(参考一)equals 方法是 java.lang.Object 类的方法.两种用法说明:(1对于字符串变量来说,使用“==”和“equals()”方法比 ...
- [03]java中的方法以及控制语句
00 Java中的语句块 语句块(有时叫做复合语句),是用花括号扩起的任意数量的简单Java语句.块确定了局部变量的作用域.块中的程序代码,作为一个整体,是要被一起执行的.块可以被嵌套在另一个块中,但 ...
- Java中的方法内联
Java中的方法内联 1. 什么是方法内联 例如有下面的原始代码: static class B { int value; final int get() { return value; } } pu ...
随机推荐
- js 将一大段时间均分为很多个小时间段
最近写项目,遇到一个将选中时间段平均分割为若干小段,然后根据小段时间在数据库查询求均值的问题,后台大哥犯懒,非说后台做不了,让我分好传给他ヾ(. ̄□ ̄)ツ゜゜゜好气呦,但还要保持微笑,我就是这么懂礼貌 ...
- JS 函数作用域及变量提升那些事!
虽然看了多次js函数作用域及变量提升的理论知识,但小编也是一知半解~ 这几天做了几道js小题,对这部分进行了从新的理解,还是有所收获的~ 主要参考书籍: <你不知道的JavaScript(上卷) ...
- MVC.NET 发布后,部署到iis ,网站中的Bootstrap的字体图标不能正常显示
时隔多日没有在博客中记录自己遇到的问题及解决方案了 ,今天给大家分享一个可能会遇到的一个鸡肋bug ! 如果你的项目是MVC并且在项目中引用了 Boostrap 框架,你在编辑发布后部署到iis的时候 ...
- jemalloc 快速上手攻略
引言 - 赠送个 Cygwin (加精) Cygwin 有它存在的合理性. 至少比 wine 好太多了. 它主要功能是在winds上面简易的模拟出linux环境, 比虚拟机 轻量一点点. 坑也不少, ...
- MySQL存储过程/存储过程与自定义函数的区别
语法: 创建存储过程: CREATE [definer = {user|current_user}] PROCEDURE sp_name ([ proc_parameter [,proc_parame ...
- 谈谈我对php通信的理解及人生小感
故事背景: 小王和小甜是亲密无间的好朋友. 小王到自己QQ群里看到一个免单活动, 自己支付宝余额不足, 遂用小甜的手机淘宝先花150元购买了活动产品. 到货后商家告诉小王, 确认收货后提供旺旺账号.支 ...
- IO(Input&Output)流の介绍
1.导读 对于设备之间的流动(即写入数据或读出数据),Java专门用Java.io包进行操作.这些数据的流动便是我们所说的数据的输入输出流(IO流). 2.数据流的处理:字节流和字符流 数据的基本单位 ...
- 顺序栈--Java实现
/*栈和队列:逻辑结构属于操作受限的线性表 *栈:特点先进后出,只允许在栈顶操作 *栈的实现方式:顺序栈和链栈 *常见的操作:进栈,出栈,获取栈顶,判空,判满,栈的容量 *栈的应用 *1.逆序相关操作 ...
- LeetCode 18. 4Sum (四数之和)
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...
- DataTable数据修改,换列
增加列 DataTable table= new DataTable(); table.Columns.Add("ID", type ...