声明式
概述
可以通过注解@ChanceFor
和动态代理调用需要重试的函数。具体步骤如下:
- 抽取需要重试的函数到一个接口类型中,通过
@ChanceFor
修饰该接口类型或者接口中的方法 - 通过
Chance.newProxy
生成接口动态代理实例 - 通过接口动态代理实例调用具体的方法
WARNING
由于 JDK 只支持基于接口实现的动态代理,这里必须把需要重试函数抽取到接口中定义,@ChanceFor 注解必须应用在接口类型或者接口方法上。
用法详解
新建一个接口:
java
// 应用在接口类上的注解
@ChanceFor(maxRetryTimes = 0)
public interface DeclarativeApi {
// 此方法会继承接口类上的注解
String useTypeAnnotation();
// 应用在接口方法上的注解
@ChanceFor(maxRetryTimes = 3, include = {RuntimeException.class}, waits = {
@ChanceFor.WaitFor(type = ChanceFor.WaitType.FIXED, multiplier = 30)
})
String useMethodAnnotation();
}
为接口提供一个实现:
java
public class DeclarativeApiImpl implements DeclarativeApi {
@Override
public String useTypeAnnotation() {
return "useTypeAnnotation";
}
@Override
public String useMethodAnnotation() {
throw new RuntimeException("Error from 'useMethodAnnotation'");
}
}
生成接口代理实例并且调用对应的方法:
java
public static void main(String[] args) {
DeclarativeApi declarativeApi = Chance.newProxy(DeclarativeApi.class, new DeclarativeApiImpl());
String r1 = declarativeApi.useTypeAnnotation();
System.out.println(r1);
try {
String r2 = declarativeApi.useMethodAnnotation();
} catch (Exception e) {
// 这里的异常e的类型为UndeclaredThrowableException - 可以参考JDK动态代理中非检查型异常相关问题
Throwable cause = e.getCause();
if (cause instanceof ChanceException) {
ChanceException ce = (ChanceException) cause;
Attempt<?, ?> lastFailedAttempt = ce.getAttempt();
System.out.println("Retry times: " + lastFailedAttempt.attemptTimes());
lastFailedAttempt.rootCauseNow().printStackTrace();
}
}
}
上面的代码最终输出结果:
shell
useTypeAnnotation
Retry times: 3
java.lang.RuntimeException: Error from 'useMethodAnnotation'
// ......省略部分异常栈信息......
TIP
注解@ChanceFor
支持的属性可以参考内置条件小节。
注意
基于零依赖
的初衷和局限于JDK
动态代理,Chance
无法天然创建非接口类型的代理,如果更偏好于创建非接口类型的代理可以参考高级特性 - 代理小节。