Try-catch是用来帮助异常处理的。这意味着它将在某种程度上帮助我们的系统变得更加健壮:尝试从意外事件中恢复。
我们怀疑在执行和指令(发送消息)时可能会发生一些事情,因此它被包含在try中。如果发生了几乎意想不到的事情,我们可以做些什么:我们编写捕获。我不认为我们调用只是为了记录异常。我认为catch块是为了给我们从错误中恢复的机会。
现在,假设我们从错误中恢复,因为我们可以修复错误。如果能再试一次,那就太好了:
try{ some_instruction(); }
catch (NearlyUnexpectedException e){
fix_the_problem();
retry;
}
这将很快陷入永恒循环,但让我们假设fix_the_problem返回true,然后重试。假设在Java中没有这样的东西,你将如何解决这个问题?你认为解决这个问题的最佳设计代码是什么?
这就像一个哲学问题,因为我已经知道Java不直接支持我所要求的内容。
我知道这里已经有很多类似的答案,我的答案也没有太大不同,但我还是会把它贴出来,因为它涉及一个具体的案例/问题。
当你在PHP中处理facebook Graph API时,你有时会得到一个错误,但立即重新尝试同样的事情会得到一个积极的结果(由于各种神奇的互联网原因,超出了这个问题的范围)。在这种情况下,不需要修复任何错误,而是简单地再试一次,因为有某种“facebook错误”。
这段代码在创建facebook会话后立即使用:
//try more than once because sometimes "facebook error"
$attempt = 3;
while($attempt-- > 0)
{
// To validate the session:
try
{
$facebook_session->validate();
$attempt = 0;
}
catch (Facebook\FacebookRequestException $ex)
{
// Session not valid, Graph API returned an exception with the reason.
if($attempt <= 0){ echo $ex->getMessage(); }
}
catch (\Exception $ex)
{
// Graph API returned info, but it may mismatch the current app or have expired.
if($attempt <= 0){ echo $ex->getMessage(); }
}
}
此外,通过让for循环计数为零($attempt——),可以很容易地改变将来的尝试次数。
生产就绪代码:
@FunctionalInterface
public interface Operation {
void doCall() throws IOException;
default void handleException(Exception e) {
//Your custom default implementation
}
public class OperationHelper {
public static void doWithRetry(int maxAttempts, Operation operation) {
for (int count = 0; count <= maxAttempts; count++) {
try {
operation.doCall();
return;
} catch (Exception e) {
if (count == maxAttempts) {
e.printStackTrace();
return;
} else {
operation.handleException(e);
}
}
}
}
}
在代码中使用默认实现:
OperationHelper.doWithRetry(10,
() -> //do your job );
当需要自定义异常句柄时使用:
OperationHelper.doWithRetry(10, new Operation() {
@Override public void doIt() {
//do some stuff
}
@Override public void handleException(Exception cause) {
//recover from the Exception
}
});
如果不是所有异常都需要重试,那么只有一些例外。如果至少要尝试一次,这里有一个替代的实用方法:
void runWithRetry(Runnable runnable, Class<Exception> exClass, int maxRetries) {
Exception err = null;
do {
maxRetries--;
try {
runnable.run();
err = null;
} catch (Exception e) {
if(exClass.isAssignableFrom(e.getClass())){
err = e;
}else {
throw e;
}
}
} while (err != null && maxRetries > 0);
if (err != null) {
throw err;
}
}
用法:
runWithRetry(() -> {
// do something
}, TimeoutException.class, 5)
Spring AOP和基于注释的解决方案:
用法(@RetryOperation是作业的自定义注释):
@RetryOperation(retryCount = 1, waitSeconds = 10)
boolean someMethod() throws Exception {
}
要做到这一点,我们需要做两件事:1。注释接口;一个春天的方面。下面是实现这些的一种方法:
标注接口:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOperation {
int retryCount();
int waitSeconds();
}
春季相位:
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;
}
}
你可以使用https://github.com/bnsd55/RetryCatch
例子:
RetryCatch retryCatchSyncRunnable = new RetryCatch();
retryCatchSyncRunnable
// For infinite retry times, just remove this row
.retryCount(3)
// For retrying on all exceptions, just remove this row
.retryOn(ArithmeticException.class, IndexOutOfBoundsException.class)
.onSuccess(() -> System.out.println("Success, There is no result because this is a runnable."))
.onRetry((retryCount, e) -> System.out.println("Retry count: " + retryCount + ", Exception message: " + e.getMessage()))
.onFailure(e -> System.out.println("Failure: Exception message: " + e.getMessage()))
.run(new ExampleRunnable());
你可以传递自己的匿名函数,而不是新的ExampleRunnable()。