Find the matching @Recover based on the number of parameters and the type of the parameters

This commit is contained in:
Piszmog
2018-08-12 10:26:31 -06:00
committed by Dave Syer
parent 9f23709958
commit f769c6de9a
2 changed files with 102 additions and 10 deletions

View File

@@ -16,10 +16,6 @@
package org.springframework.retry.annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.springframework.classify.SubclassClassifier;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.retry.ExhaustedRetryException;
@@ -27,6 +23,10 @@ import org.springframework.retry.interceptor.MethodInvocationRecoverer;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* A recoverer for method invocations based on the <code>@Recover</code> annotation. A
* suitable recovery method is one with a Throwable type as the first parameter and the
@@ -37,10 +37,11 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
* class hierarchy is chosen, so for instance if an IllegalArgumentException is being
* handled and there is a method whose first argument is RuntimeException, then it will be
* preferred over a method whose first argument is Throwable.
*
*
* @author Dave Syer
* @author Josh Long
* @author Aldo Sinanaj
* @author Randell Callahan
*/
public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationRecoverer<T> {
@@ -55,7 +56,7 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
@Override
public T recover(Object[] args, Throwable cause) {
Method method = findClosestMatch(cause.getClass());
Method method = findClosestMatch(args, cause.getClass());
if (method == null) {
throw new ExhaustedRetryException("Cannot locate recovery method", cause);
}
@@ -75,7 +76,7 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
}
}
private Method findClosestMatch(Class<? extends Throwable> cause) {
private Method findClosestMatch(Object[] args, Class<? extends Throwable> cause) {
int min = Integer.MAX_VALUE;
Method result = null;
for (Method method : methods.keySet()) {
@@ -90,6 +91,13 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
min = distance;
result = method;
}
else if (distance == min) {
boolean parametersMatch = compareParameters( args, meta.getArgCount(),
method.getParameterTypes() );
if (parametersMatch) {
result = method;
}
}
}
}
return result;
@@ -106,6 +114,22 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
return result;
}
private boolean compareParameters(Object[] args, int argCount, Class<?>[] parameterTypes) {
if (argCount == (args.length + 1)) {
int startingIndex = 0;
if (parameterTypes.length > 0 && parameterTypes[0] == Throwable.class) {
startingIndex = 1;
}
for (int i = startingIndex; i < parameterTypes.length; i++) {
if (parameterTypes[i] != args[i-1].getClass()) {
return false;
}
}
return true;
}
return false;
}
private void init(Object target, Method method) {
final Map<Class<? extends Throwable>, Method> types = new HashMap<Class<? extends Throwable>, Method>();
final Method failingMethod = method;

View File

@@ -22,9 +22,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.retry.ExhaustedRetryException;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler;
import org.springframework.retry.annotation.Retryable;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
@@ -32,6 +29,7 @@ import java.lang.reflect.Method;
/**
* @author Dave Syer
* @author Aldo Sinanaj
* @author Randell Callahan
*/
public class RecoverAnnotationRecoveryHandlerTests {
@@ -116,6 +114,28 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
@Test
public void multipleQualifyingRecoverMethods (){
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecovers.class,
"foo", String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new MultipleQualifyingRecovers(), foo);
assertEquals(1,
handler.recover(new Object[] { "Randell" }, new RuntimeException("Planned")));
}
@Test
public void multipleQualifyingRecoverMethodsReOrdered (){
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecoversReOrdered.class,
"foo", String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new MultipleQualifyingRecoversReOrdered(), foo);
assertEquals(3,
handler.recover(new Object[] { "Randell" }, new RuntimeException("Planned")));
}
private static class InAccessibleRecover {
@Retryable
@@ -232,4 +252,52 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
protected static class MultipleQualifyingRecovers {
@Retryable
public int foo(String name) {
return 0;
}
@Recover
public int fooRecover(Throwable e, String name) {
return 1;
}
@Recover
public int fooRecover(Throwable e) {
return 2;
}
@Recover
public int barRecover(Throwable e, int number) {
return 3;
}
}
protected static class MultipleQualifyingRecoversReOrdered {
@Retryable
public int foo(String name) {
return 0;
}
@Recover
public int fooRecover(Throwable e) {
return 1;
}
@Recover
public int barRecover(Throwable e, int number) {
return 2;
}
@Recover
public int fooRecover(Throwable e, String name) {
return 3;
}
}
}