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 ...
随机推荐
- 浅谈Java接口
接口(英文:Interface)是Java中非常重要的内容,初学的时候可能感受不深,但是在做项目的时候,对面向接口编程的运用就变得尤为重要,不过这是后话了.现在先讨论假如是刚刚接触接口这个概念,该怎么 ...
- 云计算---openstack各服务端口使用情况
端口占用情况 端口情况可以使用ss -tanp命令进行查看 监听的所有端口ss -tanp | grep LISTEN 基础服务 22 --SSH 3306 --MariaDB(MySQL) 2701 ...
- 阿里巴巴 Java 开发规约插件初体验
阿里巴巴 Java 开发手册 又一次来谈<阿里巴巴 Java 开发手册>,经过这大半年的版本迭代,这本阿里工程师们总结出来避免写出那么多 Bug 的规范,对于 Java 开发者简直就是必备 ...
- PE格式第五讲,手工添加节表
PE格式第五讲,手工添加节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 首先我们要用汇编编写一段汇编代码,用来生成 ...
- python爬虫爬取代理IP
# #author:wuhao # #--*------------*-- #-****#爬取代理IP并保存到Excel----#爬取当日的代理IP并保存到Excel,目标网站xicidaili.co ...
- git 合并两个仓库
我有两个仓库,一个是gitbook在写一本 一个是放在github的垃圾,这个是我想要开个人网站,但是做的还是不行https://github.com/lindexi/lindexi.github.i ...
- Numpy的小总结
1.Numpy是什么? numpy是Python的一个科学计算库,提供矩阵运算的功能. 1.1Numpy的导入 import numpy as np #一般都是用numpy的别名来进行操作 1.2Nu ...
- Java基础-Eclipse环境搭建(02)
Eclipse工具 IDE(Integrated Development Environment)集成开发环境集成了编写功能,分析功能,编译功能一体化的开发软件. 调试功能等,其中编译在保存时运行(即 ...
- 简易RPC框架-上下文
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- git合并代码解决冲突
一直测试的我,之前有接触过git命令,但是没有详细的去看这些命令的意思,只是背着在用,今天一时兴起,看到了廖雪峰的git方面的博客<a href="https://www.liaoxu ...