Find @CircuitBreaker on target class w/ JDK proxy

This commit is contained in:
Gary Russell
2018-10-23 12:01:39 -04:00
committed by Dave Syer
parent 7d5d086a33
commit f54db742f5
3 changed files with 62 additions and 54 deletions

View File

@@ -30,6 +30,7 @@ import javax.naming.OperationNotSupportedException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
@@ -167,7 +168,7 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
}
private MethodInterceptor getDelegate(Object target, Method method) {
ConcurrentMap<Method, MethodInterceptor> cachedMethods = delegates.get(target);
ConcurrentMap<Method, MethodInterceptor> cachedMethods = this.delegates.get(target);
if (cachedMethods == null) {
cachedMethods = new ConcurrentHashMap<Method, MethodInterceptor>();
}
@@ -179,7 +180,7 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
retryable = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Retryable.class);
}
if (retryable == null) {
retryable = findAnnotationOnTarget(target, method);
retryable = findAnnotationOnTarget(target, method, Retryable.class);
}
if (retryable != null) {
if (StringUtils.hasText(retryable.interceptor())) {
@@ -195,17 +196,17 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
cachedMethods.putIfAbsent(method, interceptor);
delegate = cachedMethods.get(method);
}
delegates.putIfAbsent(target, cachedMethods);
this.delegates.putIfAbsent(target, cachedMethods);
return delegate == NULL_INTERCEPTOR ? null : delegate;
}
private Retryable findAnnotationOnTarget(Object target, Method method) {
private <A extends Annotation> A findAnnotationOnTarget(Object target, Method method, Class<A> annotation) {
try {
Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(targetMethod, Retryable.class);
A retryable = AnnotatedElementUtils.findMergedAnnotation(targetMethod, annotation);
if (retryable == null) {
retryable = AnnotatedElementUtils.findMergedAnnotation(targetMethod.getDeclaringClass(),
Retryable.class);
retryable = AnnotatedElementUtils.findMergedAnnotation(targetMethod.getDeclaringClass(), annotation);
}
return retryable;
@@ -228,6 +229,9 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
template.setRetryContextCache(this.retryContextCache);
CircuitBreaker circuit = AnnotatedElementUtils.findMergedAnnotation(method, CircuitBreaker.class);
if (circuit == null) {
circuit = findAnnotationOnTarget(target, method, CircuitBreaker.class);
}
if (circuit != null) {
RetryPolicy policy = getRetryPolicy(circuit);
CircuitBreakerRetryPolicy breaker = new CircuitBreakerRetryPolicy(policy);
@@ -278,8 +282,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
if (listenersBeanNames.length > 0) {
template.setListeners(getListenersBeans(listenersBeanNames));
}
else if (globalListeners != null) {
template.setListeners(globalListeners);
else if (this.globalListeners != null) {
template.setListeners(this.globalListeners);
}
return template;
}
@@ -287,7 +291,7 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private RetryListener[] getListenersBeans(String[] listenersBeanNames) {
RetryListener[] listeners = new RetryListener[listenersBeanNames.length];
for (int i = 0; i < listeners.length; i++) {
listeners[i] = beanFactory.getBean(listenersBeanNames[i], RetryListener.class);
listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class);
}
return listeners;
}

View File

@@ -104,8 +104,8 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
result = method;
}
else if (distance == min) {
boolean parametersMatch = compareParameters(args,
meta.getArgCount(), method.getParameterTypes());
boolean parametersMatch = compareParameters(args, meta.getArgCount(),
method.getParameterTypes());
if (parametersMatch) {
result = method;
}
@@ -118,8 +118,8 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
Method method = entry.getKey();
if (method.getName().equals(this.recoverMethodName)) {
SimpleMetadata meta = entry.getValue();
if (meta.type.isAssignableFrom(cause) && compareParameters(args,
meta.getArgCount(), method.getParameterTypes())) {
if (meta.type.isAssignableFrom(cause)
&& compareParameters(args, meta.getArgCount(), method.getParameterTypes())) {
result = method;
break;
}
@@ -129,8 +129,7 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
return result;
}
private int calculateDistance(Class<? extends Throwable> cause,
Class<? extends Throwable> type) {
private int calculateDistance(Class<? extends Throwable> cause, Class<? extends Throwable> type) {
int result = 0;
Class<?> current = cause;
while (current != type && current != Throwable.class) {
@@ -140,17 +139,14 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
return result;
}
private boolean compareParameters(Object[] args, int argCount,
Class<?>[] parameterTypes) {
private boolean compareParameters(Object[] args, int argCount, Class<?>[] parameterTypes) {
if (argCount == (args.length + 1)) {
int startingIndex = 0;
if (parameterTypes.length > 0
&& Throwable.class.isAssignableFrom(parameterTypes[0])) {
if (parameterTypes.length > 0 && Throwable.class.isAssignableFrom(parameterTypes[0])) {
startingIndex = 1;
}
for (int i = startingIndex; i < parameterTypes.length; i++) {
final Object argument = i - startingIndex < args.length
? args[i - startingIndex] : null;
final Object argument = i - startingIndex < args.length ? args[i - startingIndex] : null;
if (argument == null) {
continue;
}
@@ -170,41 +166,34 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
if (retryable != null) {
this.recoverMethodName = retryable.recover();
}
ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(),
new MethodCallback() {
@Override
public void doWith(Method method)
throws IllegalArgumentException, IllegalAccessException {
Recover recover = AnnotationUtils.findAnnotation(method,
Recover.class);
if (recover != null && method.getReturnType()
.isAssignableFrom(failingMethod.getReturnType())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0 && Throwable.class
.isAssignableFrom(parameterTypes[0])) {
@SuppressWarnings("unchecked")
Class<? extends Throwable> type = (Class<? extends Throwable>) parameterTypes[0];
types.put(type, method);
RecoverAnnotationRecoveryHandler.this.methods.put(method,
new SimpleMetadata(parameterTypes.length, type));
}
else {
RecoverAnnotationRecoveryHandler.this.classifier
.setDefaultValue(method);
RecoverAnnotationRecoveryHandler.this.methods.put(method,
new SimpleMetadata(parameterTypes.length, null));
}
}
ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(), new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Recover recover = AnnotationUtils.findAnnotation(method, Recover.class);
if (recover != null && method.getReturnType().isAssignableFrom(failingMethod.getReturnType())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0 && Throwable.class.isAssignableFrom(parameterTypes[0])) {
@SuppressWarnings("unchecked")
Class<? extends Throwable> type = (Class<? extends Throwable>) parameterTypes[0];
types.put(type, method);
RecoverAnnotationRecoveryHandler.this.methods.put(method,
new SimpleMetadata(parameterTypes.length, type));
}
});
else {
RecoverAnnotationRecoveryHandler.this.classifier.setDefaultValue(method);
RecoverAnnotationRecoveryHandler.this.methods.put(method,
new SimpleMetadata(parameterTypes.length, null));
}
}
}
});
this.classifier.setTypeMap(types);
optionallyFilterMethodsBy(failingMethod.getReturnType());
}
private Recover findAnnotationOnTarget(Object target, Method method) {
try {
Method targetMethod = target.getClass().getMethod(method.getName(),
method.getParameterTypes());
Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
return AnnotationUtils.findAnnotation(targetMethod, Recover.class);
}
catch (Exception e) {
@@ -251,8 +240,7 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
result[0] = t;
startArgs = 1;
}
int length = result.length - startArgs > args.length ? args.length
: result.length - startArgs;
int length = result.length - startArgs > args.length ? args.length : result.length - startArgs;
if (length == 0) {
return result;
}

View File

@@ -103,17 +103,30 @@ public class CircuitBreakerTests {
@Bean
public Service service() {
return new Service();
return new ServiceImpl();
}
}
protected static class Service {
interface Service {
void service();
void expressionService();
int getCount();
RetryContext getContext();
}
protected static class ServiceImpl implements Service {
private int count = 0;
private RetryContext context;
@Override
@CircuitBreaker(RuntimeException.class)
public void service() {
this.context = RetrySynchronizationManager.getContext();
@@ -122,6 +135,7 @@ public class CircuitBreakerTests {
}
}
@Override
@CircuitBreaker(maxAttemptsExpression = "#{2 * ${foo:4}}", openTimeoutExpression = "#{${bar:19}000}",
resetTimeoutExpression = "#{${baz:20}000}",
exceptionExpression = "#{#root instanceof RuntimeExpression}")
@@ -129,10 +143,12 @@ public class CircuitBreakerTests {
this.count++;
}
@Override
public RetryContext getContext() {
return this.context;
}
@Override
public int getCount() {
return this.count;
}