当我必须处理数据库和网络操作的重试时,我有多个案例。无论我在哪里做,我都有以下类型的代码:
for (int iteration = 1; ; iteration++) {
try {
data = doSomethingUseful(data);
break;
} catch (SomeException | AndAnotherException e) {
if (iteration == helper.getNumberOfRetries()) {
throw e;
} else {
errorReporter.reportError("Got following error for data = {}. Continue trying after delay...", data, e);
utilities.defaultDelayForIteration(iteration);
handleSpecificCase(data);
}
}
}
问题是这个代码模式被复制粘贴到我的所有类(class)中。这真的很糟糕。我不知道如何摆脱这种 for-break-catch 复制粘贴模式,因为我通常会遇到不同的异常来处理,我想记录失败的数据(通常也是不同的方式)。
在 Java 7 中是否有避免这种复制粘贴的好方法?
编辑: 我确实使用 guice 进行依赖注入(inject)。我确实检查过异常。可能有多个变量而不是只有一个数据,而且它们都是不同类型的。
Edit2:AOP 方法对我来说看起来是最有前途的。
最佳答案
副手,我可以想到两种不同的方法:
如果异常处理的差异可以声明方式表达,您可以使用 AOP 围绕您的方法编写异常处理代码。然后,您的业务代码可能如下所示:
@Retry(times = 3, loglevel = LogLevel.INFO)
List<User> getActiveUsers() throws DatabaseException {
// talk to the database
}
优点是给一个方法添加重试行为真的很容易,缺点是编织通知的复杂性(你只需要实现一次。如果你使用依赖注入(inject)库,很可能会提供方法拦截支持)。
另一种方法是使用命令模式:
abstract class Retrieable<I,O> {
private final LogLevel logLevel;
protected Retrieable(LogLevel loglevel) {
this.logLevel = loglevel;
}
protected abstract O call(I input);
// subclasses may override to perform custom logic.
protected void handle(RuntimeException e) {
// log the exception.
}
public O execute(I input) {
for (int iteration = 1; ; iteration++) {
try {
return call(input);
} catch (RuntimeException e) {
if (iteration == helper.getNumberOfRetries()) {
throw e;
} else {
handle();
utilities.defaultDelayForIteration(iteration);
}
}
}
}
}
命令模式的问题在于方法参数。您只能使用一个参数,而且泛型对于调用者来说相当笨拙。此外,它不适用于已检查的异常。从好的方面来说,没有花哨的 AOP 东西:-)
关于Java:如何在没有复制粘贴代码的情况下处理重试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9539845/