Browse Source

Consistently detect resilience annotations on interfaces

Closes gh-36233
pull/36234/head
Juergen Hoeller 2 days ago
parent
commit
516bf1c606
  1. 4
      spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java
  2. 4
      spring-context/src/main/java/org/springframework/resilience/annotation/RetryAnnotationBeanPostProcessor.java
  3. 66
      spring-context/src/test/java/org/springframework/resilience/ConcurrencyLimitTests.java
  4. 1
      spring-context/src/test/java/org/springframework/resilience/RetryInterceptorTests.java

4
spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java

@ -101,14 +101,14 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA @@ -101,14 +101,14 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA
interceptor = holder.methodInterceptors.get(method);
if (interceptor == null) {
boolean perMethod = false;
ConcurrencyLimit annotation = AnnotatedElementUtils.getMergedAnnotation(method, ConcurrencyLimit.class);
ConcurrencyLimit annotation = AnnotatedElementUtils.findMergedAnnotation(method, ConcurrencyLimit.class);
if (annotation != null) {
perMethod = true;
}
else {
interceptor = holder.classInterceptor;
if (interceptor == null) {
annotation = AnnotatedElementUtils.getMergedAnnotation(targetClass, ConcurrencyLimit.class);
annotation = AnnotatedElementUtils.findMergedAnnotation(targetClass, ConcurrencyLimit.class);
}
}
if (interceptor == null) {

4
spring-context/src/main/java/org/springframework/resilience/annotation/RetryAnnotationBeanPostProcessor.java

@ -93,9 +93,9 @@ public class RetryAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd @@ -93,9 +93,9 @@ public class RetryAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
return retrySpec;
}
Retryable retryable = AnnotatedElementUtils.getMergedAnnotation(method, Retryable.class);
Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);
if (retryable == null) {
retryable = AnnotatedElementUtils.getMergedAnnotation(targetClass, Retryable.class);
retryable = AnnotatedElementUtils.findMergedAnnotation(targetClass, Retryable.class);
if (retryable == null) {
return null;
}

66
spring-context/src/test/java/org/springframework/resilience/ConcurrencyLimitTests.java

@ -23,9 +23,11 @@ import java.util.concurrent.atomic.AtomicInteger; @@ -23,9 +23,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ConcurrencyThrottleInterceptor;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -77,6 +79,32 @@ class ConcurrencyLimitTests { @@ -77,6 +79,32 @@ class ConcurrencyLimitTests {
assertThat(target.current).hasValue(0);
}
@Test
void withPostProcessorForMethodWithInterface() {
AnnotatedInterface proxy = createProxy(AnnotatedMethodBeanWithInterface.class, AnnotatedInterface.class, false);
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
List<CompletableFuture<?>> futures = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
futures.add(CompletableFuture.runAsync(proxy::concurrentOperation));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
assertThat(target.current).hasValue(0);
}
@Test
void withPostProcessorForMethodWithInterfaceAndDefaultTargetClass() {
AnnotatedInterface proxy = createProxy(AnnotatedMethodBeanWithInterface.class, AnnotatedInterface.class, true);
AnnotatedMethodBeanWithInterface target = (AnnotatedMethodBeanWithInterface) AopProxyUtils.getSingletonTarget(proxy);
List<CompletableFuture<?>> futures = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
futures.add(CompletableFuture.runAsync(proxy::concurrentOperation));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
assertThat(target.current).hasValue(0);
}
@Test
void withPostProcessorForMethodWithUnboundedConcurrency() {
AnnotatedMethodBean proxy = createProxy(AnnotatedMethodBean.class);
@ -201,12 +229,21 @@ class ConcurrencyLimitTests { @@ -201,12 +229,21 @@ class ConcurrencyLimitTests {
private static <T> T createProxy(Class<T> beanClass) {
return createProxy(beanClass, beanClass, true);
}
private static <T> T createProxy(Class<?> beanClass, Class<T> exposedClass, boolean proxyTargetClass) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
if (exposedClass.isInterface() && proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(bf);
}
bf.registerBeanDefinition("bean", new RootBeanDefinition(beanClass));
ConcurrencyLimitBeanPostProcessor bpp = new ConcurrencyLimitBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
return bf.getBean(beanClass);
T proxy = bf.getBean(exposedClass);
assertThat(proxyTargetClass ? AopUtils.isCglibProxy(proxy) : AopUtils.isJdkDynamicProxy(proxy)).isTrue();
return proxy;
}
@ -274,6 +311,33 @@ class ConcurrencyLimitTests { @@ -274,6 +311,33 @@ class ConcurrencyLimitTests {
}
static class AnnotatedMethodBeanWithInterface implements AnnotatedInterface {
final AtomicInteger current = new AtomicInteger();
@Override
public void concurrentOperation() {
if (current.incrementAndGet() > 2) {
throw new IllegalStateException();
}
try {
Thread.sleep(100);
}
catch (InterruptedException ex) {
throw new IllegalStateException(ex);
}
current.decrementAndGet();
}
}
interface AnnotatedInterface {
@ConcurrencyLimit(2)
void concurrentOperation();
}
@ConcurrencyLimit(2)
static class AnnotatedClassBean {

1
spring-context/src/test/java/org/springframework/resilience/RetryInterceptorTests.java

@ -523,7 +523,6 @@ class RetryInterceptorTests { @@ -523,7 +523,6 @@ class RetryInterceptorTests {
int counter = 0;
@Retryable(maxRetries = 5, delay = 10)
@Override
public void retryOperation() throws IOException {
counter++;

Loading…
Cancel
Save