你如何实现重新尝试?

And*_*ias 185 java exception-handling exception try-catch

Try-catch旨在帮助进行异常处理.这意味着它将以某种方式帮助我们的系统更加健壮:尝试从意外事件中恢复.

我们怀疑在执行和指令(发送消息)时可能会发生某些事情,因此它会被包含在try中.如果发生几乎意外的事情,我们可以做点什么:我们写下了捕获.我认为我们没有打电话来记录异常.我认为catch块意味着让我们有机会从错误中恢复.

现在,假设我们可以从错误中恢复,因为我们可以修复错误.重做是非常好的:

try{ some_instruction(); }
catch (NearlyUnexpectedException e){
   fix_the_problem();
   retry;
}
Run Code Online (Sandbox Code Playgroud)

这将很快落入永恒循环,但是假设fix_the_problem返回true,那么我们重试.鉴于Java中没有这样的东西,你将如何解决这个问题?解决这个问题的最佳设计代码是什么?

这就像一个哲学问题,因为我已经知道我所要求的并不是Java直接支持的.

Roh*_*ain 280

你需要将你的try-catch内部包围在这样的while循环中: -

int count = 0;
int maxTries = 3;
while(true) {
    try {
        // Some Code
        // break out of loop, or return, on success
    } catch (SomeException e) {
        // handle exception
        if (++count == maxTries) throw e;
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经采取countmaxTries避免遇到无限循环,以防异常继续发生在你的try block.

  • @AndresFarias ..是的,这个答案中最重要的一点是包含一个`maxTries`.否则,如果用户不断提供错误输入,它将遇到"无限循环",因此不会退出.不过你很受欢迎.:) (6认同)
  • 改变这个。`if(++count >= maxTries)` (4认同)
  • 我一开始就想过这样的事情,没有maxTries.谢谢你的回答! (3认同)
  • 效果很好!对于初学者:如果您得到正无限循环,请检查您是否添加了“break;” 在“try”块的最后。 (3认同)
  • 是否可以在catch中添加Thread.sleep()函数.因为在某些情况下,等待Selenium库中的页面响应变得至关重要.谢谢. (2认同)

ach*_*ach 52

强制性的"企业家"解决方案:

public abstract class Operation {
    abstract public void doIt();
    public void handleException(Exception cause) {
        //default impl: do nothing, log the exception, etc.
    }
}

public class OperationHelper {
    public static void doWithRetry(int maxAttempts, Operation operation) {
        for (int count = 0; count < maxAttempts; count++) {
            try {
                operation.doIt();
                count = maxAttempts; //don't retry
            } catch (Exception e) {
                operation.handleException(e);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并致电:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 如果最后一次重试失败,您应该重新抛出异常,就像在给出的其他答案中所做的那样. (6认同)

mer*_*ike 33

像往常一样,最好的设计取决于具体情况.通常,我写的东西如下:

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 因为我只想抛出最后的尝试,因此catch块需要这个条件,使条件成为冗余. (8认同)
  • 我认为那里不需要 `continue` .. 你可以简单地翻转 if 条件。 (4认同)

Ale*_*exR 19

虽然try/catch进入while是众所周知的好策略我想建议你递归调用:

void retry(int i, int limit) {
    try {

    } catch (SomeException e) {
        // handle exception
        if (i >= limit) {
            throw e;  // variant: wrap the exception, e.g. throw new RuntimeException(e);
        }
        retry(i++, limit);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 递归比这个用例的循环更好? (39认同)
  • 使用递归作为单纯迭代的替代是不好的做法.当您想要推送和弹出一些数据时,可以使用递归. (8认同)
  • 堆栈跟踪在这个上看起来有点奇怪,因为它不会有被递归方法的"限制"计数吗?与循环版本相反,循环版本将抛出"原始"级别...... (7认同)
  • 当然在纸上看起来很优雅,但我不确定递归是否是正确的方法. (7认同)
  • 我不明白为什么递归这里.无论如何,我认为它可以简化为:`void retry(int times){(...)if(times == 0)throw w; 重试(次 - );` (3认同)

Jon*_*han 17

您通过Failsafe处理的确切方案:

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(NearlyUnexpectedException.class);

Failsafe.with(retryPolicy)
  .onRetry((r, f) -> fix_the_problem())
  .run(() -> some_instruction());
Run Code Online (Sandbox Code Playgroud)

很简单.

  • 非常好的图书馆. (4认同)

yeg*_*256 16

您可以使用jcabi-aspects的 AOP和Java注释(我是开发人员):

@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
  return url.openConnection().getContent();
}
Run Code Online (Sandbox Code Playgroud)

您也可以使用@Loggable@LogException注释.

  • 有没有办法在尝试失败时"修复"错误(做一些可以修复下一次尝试的收养)?在catch块中看到问题:`fix_the_problem();` (2认同)

Viv*_*thi 9

Spring AOP和基于注解的解决方案:

用法(@RetryOperation是我们对该作业的自定义注释):

@RetryOperation(retryCount = 1, waitSeconds = 10)
boolean someMethod() throws Exception {
}
Run Code Online (Sandbox Code Playgroud)

我们需要两件事来完成此任务:1. 一个注释接口,2. 一个 spring 方面。这是实现这些的一种方法:

注释接口:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOperation {
    int retryCount();
    int waitSeconds();
}
Run Code Online (Sandbox Code Playgroud)

春季方面:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Aspect @Component 
public class RetryAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryAspect.class);

    @Around(value = "@annotation(RetryOperation)")
    public Object retryOperation(ProceedingJoinPoint joinPoint) throws Throwable {

        Object response = null;
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        RetryOperation annotation = method.getAnnotation(RetryOperation.class);
        int retryCount = annotation.retryCount();
        int waitSeconds = annotation.waitSeconds();
        boolean successful = false;

        do {
            try {
                response = joinPoint.proceed();
                successful = true;
            } catch (Exception ex) {
                LOGGER.info("Operation failed, retries remaining: {}", retryCount);
                retryCount--;
                if (retryCount < 0) {
                    throw ex;
                }
                if (waitSeconds > 0) {
                    LOGGER.info("Waiting for {} second(s) before next retry", waitSeconds);
                    Thread.sleep(waitSeconds * 1000l);
                }
            }
        } while (!successful);

        return response;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*n P 6

大多数答案基本相同.我也是,但这是我喜欢的形式

boolean completed = false;
Throwable lastException = null;
for (int tryCount=0; tryCount < config.MAX_SOME_OPERATION_RETRIES; tryCount++)
{
    try {
        completed = some_operation();
        break;
    }
    catch (UnlikelyException e) {
        lastException = e;
        fix_the_problem();
    }
}
if (!completed) {
    reportError(lastException);
}
Run Code Online (Sandbox Code Playgroud)

  • @JoachimSauer真的.你可以`if(tryCount <max)fix()` - 但这是一般方法的格式; 细节取决于具体案例.还有一个基于番石榴的[Retryer](http://code.google.com/p/google-guava-retryer/)我一直在关注. (2认同)

Yog*_*ngh 5

使用while带有本地status标志的循环。将标志初始化为falsetrue在操作成功时将其设置为如下所示:

  boolean success  = false;
  while(!success){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }
Run Code Online (Sandbox Code Playgroud)

这将不断重试,直到成功。

如果您只想重试特定次数,那么也可以使用计数器:

  boolean success  = false;
  int count = 0, MAX_TRIES = 10;
  while(!success && count++ < MAX_TRIES){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }
  if(!success){
    //It wasn't successful after 10 retries
  }
Run Code Online (Sandbox Code Playgroud)

如果不成功,这将最多尝试 10 次,然后如果成功则退出。