我有一些功能在catch
块中执行相同的操作。
private void fun1(int a){
try{
// do api calls
}catch(Exception e){
refreshToken();
fun1(a);
}
}
private int fun2(int a, String b){
try{
// do api calls
}catch(Exception e){
refreshToken();
fun2(a,b);
}
}
private void fun3(String a, long b, char c){
try{
// do api calls
}catch(Exception e){
refreshToken();
fun3(a,b,c);
}
}
在这里,当应用程序令牌到期时,我需要调用一个函数来获取新令牌,并需要调用父函数。
在catch块中,代码被重复。所以我想在一个功能中执行这些操作。为此,我需要传递函数和参数。是否可以在Java
中做到这一点?
答案 0 :(得分:2)
如果您更频繁地需要这种功能,则可能需要构建自己的支持实用程序(然后您还可以根据需要对其进行改进),例如:
public class Retry {
public static <T> T endlessTryCall(Callable<T> callable, Consumer<Exception> exceptionHandler) {
for(;;) try {
return callable.call();
} catch (Exception e) {
exceptionHandler.accept(e);
}
}
public static void endlessTryRun(Runnable runnable, Consumer<Exception> exceptionHandler) {
for(;;) try {
runnable.run();
return;
} catch (Exception e) {
exceptionHandler.accept(e);
}
}
}
您的函数将如下所示:
private void fun1(int a){
Retry.endlessTryRun(() -> {
// the API calls
}, e -> refreshToken());
}
private int fun2(int a, String b){
return Retry.endlessTryCall(() -> {
// the API calls
return ...;
}, e -> refreshToken());
}
private void fun3(String a, long b, char c){
Retry.endlessTryRun(() -> {
// the API calls
}, e -> refreshToken());
}
其他改进可以是使用tryCall(numberOfTrials, ...)
函数之类的功能,或者是另一个可以按您所需方式处理异常的函数。演示后者:
private void fun1(int a){
refreshTokenOnException(() -> {
// the API calls
});
}
private int fun2(int a, String b){
return refreshTokenOnException(() -> {
// the API calls
return ...;
});
}
private void fun3(String a, long b, char c){
refreshTokenOnException(() -> {
// the API calls
});
}
private <T> T refreshTokenOnException(Callable<T> callable) {
return Retry.endlessTryCall(callable, e -> refreshToken());
}
private void refreshTokenOnException(Runnable runnable) {
Retry.endlessTryRun(runnable, e -> refreshToken());
}
使用这种实用程序的好处可能令人怀疑。我认为所做的工作有些冗长。但是话又说回来,必须为其找到合适的名称……所以……这依旧取决于。
答案 1 :(得分:1)
您在这里已经做过最合理的事情,将通用代码提取到方法refreshToken()
中,并在必要时在每个位置调用此方法。这就是我们的操作方式,并且可以接受单个方法调用的“代码重复”。将代码分解成更复杂的内容并不是很有建设性的,即使每种情况下复杂的代码由很多不同的样板组成。
您唯一应该避免的事情是通过递归重复该操作。由于当前令牌的失效可能会发生任意次数,因此,由于您无法控制评估所需的时间,因此可能会有任意次数的递归,从而增加了StackOverflowError
的风险。同样,递归调用在这里确实是可以避免的代码。
只需使用循环即可。
private void fun1(final int a) {
for(;;) try {
// do api calls, don't modify a
return;
} catch(Exception e) {
refreshToken();
}
}
private int fun2(final int a, final String b) {
try {
// do api calls, don't modify a nor b
return result;
} catch(Exception e) {
refreshToken();
}
}
private void fun3(final String a, final long b, final char c) {
for(;;) try {
// do api calls, don't modify a, b, nor c
return;
} catch(Exception e) {
refreshToken();
}
}
答案 2 :(得分:0)
您可以采取解决方法,并执行“ switch ... case”操作,并将switch键作为参数...
<kotlin.version>1.3.30</kotlin.version>
<spring.version>5.1.4.RELEASE</spring.version>
} }
您将需要输入所有3个函数的所有参数作为参数...
另一个选择是使用Java反射
答案 3 :(得分:0)
package be.test;
public class FuncTest {
private void fun1(int a) {
// do api calls
}
private int fun2(int a, String b) {
// do api calls
return 0;
}
private void fun3(String a, long b, char c) {
// do api calls
}
private void refreshToken() {
// do your refresh token logic
}
// functional interface
public interface MyFunc {
public void doIt();
}
public void callWithRefresh(MyFunc func)
{
try {
func.doIt();
} catch (Exception e) {
refreshToken();
func.doIt();
}
}
public static void main(String[] args) {
FuncTest ftest=new FuncTest();
ftest.callWithRefresh(()->ftest.fun1(1));
ftest.callWithRefresh(()->ftest.fun2(1,"hopla"));
ftest.callWithRefresh(()->ftest.fun3("hopla",1,'c'));
}
}
callWithRefresh
包含在发生异常时调用refreshToken的通用逻辑。 (希望异常原因与令牌过期有关:)
MyFunc只是一个能够调用doIt()的接口。不必在任何地方实现此接口。
答案 4 :(得分:0)
考虑使用Failsafe之类的库,该库在处理不同的错误情况时具有更大的灵活性。
首先,您必须声明一个重试策略,该策略定义应处理哪些故障以及何时应执行重试。您只想在令牌过期时重试,而不是在引发另一个错误时重试。
RetryPolicy<Object> retryPolicy = new RetryPolicy<>()
.handle(TokenExpiredException.class)
.withMaxRetries(3);
并按以下方式调用
Failsafe.with(retryPolicy)
.onFailure(e -> refreshToken())
.run(() -> fun1(42));
此外,它可以与(经检查的)供应商一起使用,就像您使用fun2
时一样(它返回一个整数):
int result = Failsafe.with(retryPolicy)
.onFailure(e -> refreshToken())
.get(() -> fun2(12, "cat"));