Skip to content

Commit 73ad1eb

Browse files
committed
Introduce @⁠ConcurrencyLimit(limit) and revise contribution
This commit introduces a new `limit` attribute in @⁠ConcurrencyLimit as an alias for the existing `value` attribute. This commit also renames the `valueString` attribute to `limitString`. See gh-35461 See gh-35470
1 parent 91f112a commit 73ad1eb

File tree

3 files changed

+33
-19
lines changed

3 files changed

+33
-19
lines changed

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.lang.annotation.Target;
2424

2525
import org.springframework.aot.hint.annotation.Reflective;
26+
import org.springframework.core.annotation.AliasFor;
2627

2728
/**
2829
* A common annotation specifying a concurrency limit for an individual method,
@@ -43,6 +44,7 @@
4344
*
4445
* @author Juergen Hoeller
4546
* @author Hyunsang Han
47+
* @author Sam Brannen
4648
* @since 7.0
4749
* @see EnableResilientMethods
4850
* @see ConcurrencyLimitBeanPostProcessor
@@ -55,20 +57,33 @@
5557
@Reflective
5658
public @interface ConcurrencyLimit {
5759

60+
/**
61+
* Alias for {@link #limit()}.
62+
* <p>Intended to be used when no other attributes are needed &mdash; for
63+
* example, {@code @ConcurrencyLimit(5)}.
64+
* @see #limitString()
65+
*/
66+
@AliasFor("limit")
67+
int value() default 1;
68+
5869
/**
5970
* The applicable concurrency limit: 1 by default,
6071
* effectively locking the target instance for each method invocation.
6172
* <p>Specify a limit higher than 1 for pool-like throttling, constraining
6273
* the number of concurrent invocations similar to the upper bound of a pool.
74+
* @see #value()
75+
* @see #limitString()
6376
*/
64-
int value() default 1;
77+
@AliasFor("value")
78+
int limit() default 1;
6579

6680
/**
67-
* The concurrency limit as a configurable String.
68-
* A non-empty value specified here overrides the {@link #value()} attribute.
81+
* The concurrency limit, as a configurable String.
82+
* <p>A non-empty value specified here overrides the {@link #limit()} (or
83+
* {@link #value()}) attribute.
6984
* <p>This supports Spring-style "${...}" placeholders as well as SpEL expressions.
70-
* @see #value()
85+
* @see #limit()
7186
*/
72-
String valueString() default "";
87+
String limitString() default "";
7388

7489
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA
5353

5454
private @Nullable StringValueResolver embeddedValueResolver;
5555

56+
5657
public ConcurrencyLimitBeanPostProcessor() {
5758
setBeforeExistingAdvisors(true);
5859

@@ -94,19 +95,19 @@ private class ConcurrencyLimitInterceptor implements MethodInterceptor {
9495
interceptor = cache.methodInterceptors.get(method);
9596
if (interceptor == null) {
9697
boolean perMethod = false;
97-
ConcurrencyLimit limit = AnnotatedElementUtils.getMergedAnnotation(method, ConcurrencyLimit.class);
98-
if (limit != null) {
98+
ConcurrencyLimit annotation = AnnotatedElementUtils.getMergedAnnotation(method, ConcurrencyLimit.class);
99+
if (annotation != null) {
99100
perMethod = true;
100101
}
101102
else {
102103
interceptor = cache.classInterceptor;
103104
if (interceptor == null) {
104-
limit = AnnotatedElementUtils.getMergedAnnotation(targetClass, ConcurrencyLimit.class);
105+
annotation = AnnotatedElementUtils.getMergedAnnotation(targetClass, ConcurrencyLimit.class);
105106
}
106107
}
107108
if (interceptor == null) {
108-
Assert.state(limit != null, "No @ConcurrencyLimit annotation found");
109-
int concurrencyLimit = parseInt(limit.value(), limit.valueString());
109+
Assert.state(annotation != null, "No @ConcurrencyLimit annotation found");
110+
int concurrencyLimit = parseInt(annotation.limit(), annotation.limitString());
110111
interceptor = new ConcurrencyThrottleInterceptor(concurrencyLimit);
111112
if (!perMethod) {
112113
cache.classInterceptor = interceptor;

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
21-
import java.util.Properties;
2221
import java.util.concurrent.CompletableFuture;
2322
import java.util.concurrent.atomic.AtomicInteger;
2423

@@ -30,7 +29,7 @@
3029
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3130
import org.springframework.beans.factory.support.RootBeanDefinition;
3231
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
33-
import org.springframework.core.env.PropertiesPropertySource;
32+
import org.springframework.core.testfixture.env.MockPropertySource;
3433
import org.springframework.resilience.annotation.ConcurrencyLimit;
3534
import org.springframework.resilience.annotation.ConcurrencyLimitBeanPostProcessor;
3635
import org.springframework.resilience.annotation.EnableResilientMethods;
@@ -104,18 +103,16 @@ void withPostProcessorForClass() {
104103

105104
@Test
106105
void withPlaceholderResolution() {
107-
Properties props = new Properties();
108-
props.setProperty("test.concurrency.limit", "3");
109-
106+
MockPropertySource mockPropertySource = new MockPropertySource("test").withProperty("test.concurrency.limit", "3");
110107
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
111-
ctx.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("test", props));
108+
ctx.getEnvironment().getPropertySources().addFirst(mockPropertySource);
112109
ctx.register(PlaceholderTestConfig.class, PlaceholderBean.class);
113110
ctx.refresh();
114111

115112
PlaceholderBean proxy = ctx.getBean(PlaceholderBean.class);
116113
PlaceholderBean target = (PlaceholderBean) AopProxyUtils.getSingletonTarget(proxy);
117114

118-
// Test with limit=3 from properties
115+
// Test with limit=3 from MockPropertySource
119116
List<CompletableFuture<?>> futures = new ArrayList<>(10);
120117
for (int i = 0; i < 10; i++) {
121118
futures.add(CompletableFuture.runAsync(proxy::concurrentOperation));
@@ -125,6 +122,7 @@ void withPlaceholderResolution() {
125122
ctx.close();
126123
}
127124

125+
128126
static class NonAnnotatedBean {
129127

130128
AtomicInteger counter = new AtomicInteger();
@@ -197,7 +195,7 @@ public void otherOperation() {
197195
current.decrementAndGet();
198196
}
199197

200-
@ConcurrencyLimit(1)
198+
@ConcurrencyLimit(limit = 1)
201199
public void overrideOperation() {
202200
if (currentOverride.incrementAndGet() > 1) {
203201
throw new IllegalStateException();
@@ -222,7 +220,7 @@ static class PlaceholderBean {
222220

223221
AtomicInteger current = new AtomicInteger();
224222

225-
@ConcurrencyLimit(valueString = "${test.concurrency.limit}")
223+
@ConcurrencyLimit(limitString = "${test.concurrency.limit}")
226224
public void concurrentOperation() {
227225
if (current.incrementAndGet() > 3) { // Assumes test.concurrency.limit=3
228226
throw new IllegalStateException();

0 commit comments

Comments
 (0)