Java:如何在没有复制粘贴代码的情况下处理重试?
我有多个案件需要对数据库和网络操作进行重审。无论我在哪里做,我都有以下类型的代码:Java:如何在没有复制粘贴代码的情况下处理重试?,java,exception,copy-paste,Java,Exception,Copy Paste,我有多个案件需要对数据库和网络操作进行重审。无论我在哪里做,我都有以下类型的代码: for (int iteration = 1; ; iteration++) { try { data = doSomethingUseful(data); break; } catch (SomeException | AndAnotherException e) { if (iteration =
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);
}
}
}
问题是这个代码模式被复制粘贴到我的所有类中。这真的很糟糕。我不知道如何摆脱这种break-catch-copy-paste模式,因为我通常会处理不同的异常,我想记录失败的数据(通常也有不同的方式)
在Java7中有没有避免这种复制粘贴的好方法
编辑:我确实使用guice进行依赖注入。我确实检查过例外情况。可以有多个变量而不是一个数据,并且它们都是不同类型的
Edit2:AOP方法看起来对我来说是最有希望的。让你的
doSomething
实现一个接口,例如可运行的,并创建一个包含你上面代码的方法,用doSomething
替换为接口。运行(数据)
扩展已经讨论过的方法,像这样的东西怎么样(这个上网本上没有IDE,所以把它当作伪代码…)
我可以马上想到两种不同的方法:
如果异常处理中的差异可以用声明方式表示,那么可以使用AOP在方法周围编织异常处理代码。然后,您的业务代码可能如下所示:
@Retry(times = 3, loglevel = LogLevel.INFO)
List<User> getActiveUsers() throws DatabaseException {
// talk to the database
}
@重试(次数=3,日志级别=loglevel.INFO)
List getActiveUsers()引发DatabaseException{
//与数据库对话
}
优点是很容易将重试行为添加到方法中,缺点是编织建议的复杂性(您只需实现一次。如果您使用的是依赖项注入库,它很可能会提供方法拦截支持)
另一种方法是使用命令模式:
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);
}
}
}
}
}
抽象类可检索{
私人最终日志级别日志级别;
受保护可检索(日志级别日志级别){
this.logLevel=logLevel;
}
受保护的抽象O调用(I输入);
//子类可以重写以执行自定义逻辑。
受保护的无效句柄(运行时异常e){
//记录异常。
}
公共O执行(I输入){
for(int迭代=1;迭代++){
试一试{
回电(输入);
}捕获(运行时异常e){
if(迭代==helper.getNumberOfRetries()){
投掷e;
}否则{
句柄();
实用程序.defaultDelayForIteration(迭代);
}
}
}
}
}
命令模式的问题在于方法参数。您仅限于一个参数,并且泛型对于调用方来说相当笨拙。此外,它不会与选中的异常一起工作。从好的方面来说,没有华丽的AOP内容:-)正如前面所建议的,AOP和Java注释是一个不错的选择。我建议从以下位置使用读取机制:
请阅读这篇博文:我已经实现了下面的RetryLogic类,它提供可重用的重试逻辑并支持参数,因为要重试的代码位于传入的委托中
/**
* Generic retry logic. Delegate must throw the specified exception type to trigger the retry logic.
*/
public class RetryLogic<T>
{
public static interface Delegate<T>
{
T call() throws Exception;
}
private int maxAttempts;
private int retryWaitSeconds;
@SuppressWarnings("rawtypes")
private Class retryExceptionType;
public RetryLogic(int maxAttempts, int retryWaitSeconds, @SuppressWarnings("rawtypes") Class retryExceptionType)
{
this.maxAttempts = maxAttempts;
this.retryWaitSeconds = retryWaitSeconds;
this.retryExceptionType = retryExceptionType;
}
public T getResult(Delegate<T> caller) throws Exception {
T result = null;
int remainingAttempts = maxAttempts;
do {
try {
result = caller.call();
} catch (Exception e){
if (e.getClass().equals(retryExceptionType))
{
if (--remainingAttempts == 0)
{
throw new Exception("Retries exausted.");
}
else
{
try {
Thread.sleep((1000*retryWaitSeconds));
} catch (InterruptedException ie) {
}
}
}
else
{
throw e;
}
}
} while (result == null && remainingAttempts > 0);
return result;
}
}
/**
*通用重试逻辑。委托必须抛出指定的异常类型才能触发重试逻辑。
*/
公共类RetryLogic
{
公共静态接口委托
{
T call()抛出异常;
}
私有int-maxtures;
私有int retryWaitSeconds;
@抑制警告(“原始类型”)
私有类retryExceptionType;
公共RetryLogic(int-maxAttempts、int-retryWaitSeconds、@SuppressWarnings(“rawtypes”)类retryExceptionType)
{
this.maxAttempts=maxAttempts;
this.retryWaitSeconds=retryWaitSeconds;
this.retryExceptionType=retryExceptionType;
}
公共T getResult(委托调用方)引发异常{
T结果=null;
int remainingAttempts=最大尝试次数;
做{
试一试{
结果=caller.call();
}捕获(例外e){
如果(例如getClass()等于(retryExceptionType))
{
如果(--remainingAttempts==0)
{
抛出新异常(“重试次数已被检查”);
}
其他的
{
试一试{
睡眠((1000*retryWaitSeconds));
}捕获(中断异常ie){
}
}
}
其他的
{
投掷e;
}
}
}while(result==null&&remaingattempts>0);
返回结果;
}
}
下面是一个使用示例。要重试的代码在call方法中
private MyResultType getDataWithRetry(final String parameter) throws Exception {
return new RetryLogic<MyResultType>(5, 15, Exception.class).getResult(new RetryLogic.Delegate<MyResultType> () {
public MyResultType call() throws Exception {
return dataLayer.getData(parameter);
}});
}
private MyResultType getDataWithRetry(最终字符串参数)引发异常{
返回新的RetryLogic(5,15,Exception.class).getResult(新的RetryLogic.Delegate(){
公共MyResultType调用()引发异常{
返回dataLayer.getData(参数);
}});
}
如果您只想在特定类型的异常发生时重试(并在所有其他类型的异常上失败),RetryLogic类支持异常类参数。请查看:
此方法应适用于大多数用例:
public static <T> T executeWithRetry(final Callable<T> what, final int nrImmediateRetries,
final int nrTotalRetries, final int retryWaitMillis, final int timeoutMillis)
public static T executeWithRetry(final Callable what,final int nrimediateretries,
最终整数nrTotalRetries、最终整数retryWaitMillis、最终整数timeoutMillis)
您可以使用此实用程序轻松地实现一个方面,只需更少的代码即可实现。我想补充一点。大多数异常(99.999%)意味着您的代码或环境存在严重问题,需要管理员注意。如果您的代码无法连接到数据库,则可能是错误配置
private MyResultType getDataWithRetry(final String parameter) throws Exception {
return new RetryLogic<MyResultType>(5, 15, Exception.class).getResult(new RetryLogic.Delegate<MyResultType> () {
public MyResultType call() throws Exception {
return dataLayer.getData(parameter);
}});
}
public static <T> T executeWithRetry(final Callable<T> what, final int nrImmediateRetries,
final int nrTotalRetries, final int retryWaitMillis, final int timeoutMillis)
// client code - what you write a lot
public class SomeDao {
public SomeReturn saveObject( final SomeObject obj ) throws RetryException {
Retry<SomeReturn> retry = new Retry<SomeReturn>() {
public SomeReturn execute() throws Exception {
try {
// doSomething
return someReturn;
} catch( SomeExpectedBadExceptionNotWorthRetrying ex ) {
throw new NoRetryException( ex ); // optional exception block
}
}
}
return retry.run();
}
}
// framework - what you write once
public abstract class Retry<T> {
public static final int MAX_RETRIES = 3;
private int tries = 0;
public T execute() throws Exception;
public T run() throws RetryException {
try {
return execute();
} catch( NoRetryException ex ) {
throw ex;
} catch( Exception ex ) {
tries++;
if( MAX_RETRIES == tries ) {
throw new RetryException("Maximum retries exceeded", ex );
} else {
return run();
}
}
}
}