|
|
|
@ -17,9 +17,10 @@ |
|
|
|
package org.springframework.resilience.annotation; |
|
|
|
package org.springframework.resilience.annotation; |
|
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
|
|
|
|
import java.util.Collections; |
|
|
|
|
|
|
|
import java.util.IdentityHashMap; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentMap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.aopalliance.intercept.MethodInterceptor; |
|
|
|
import org.aopalliance.intercept.MethodInterceptor; |
|
|
|
import org.aopalliance.intercept.MethodInvocation; |
|
|
|
import org.aopalliance.intercept.MethodInvocation; |
|
|
|
@ -35,7 +36,6 @@ import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; |
|
|
|
import org.springframework.context.EmbeddedValueResolverAware; |
|
|
|
import org.springframework.context.EmbeddedValueResolverAware; |
|
|
|
import org.springframework.core.annotation.AnnotatedElementUtils; |
|
|
|
import org.springframework.core.annotation.AnnotatedElementUtils; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.ConcurrentReferenceHashMap; |
|
|
|
|
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.util.StringValueResolver; |
|
|
|
import org.springframework.util.StringValueResolver; |
|
|
|
|
|
|
|
|
|
|
|
@ -74,29 +74,29 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA |
|
|
|
|
|
|
|
|
|
|
|
private class ConcurrencyLimitInterceptor implements MethodInterceptor { |
|
|
|
private class ConcurrencyLimitInterceptor implements MethodInterceptor { |
|
|
|
|
|
|
|
|
|
|
|
private final ConcurrentMap<Object, ConcurrencyThrottleCache> cachePerInstance = |
|
|
|
private final Map<Object, ConcurrencyThrottleHolder> holderPerInstance = |
|
|
|
new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK); |
|
|
|
Collections.synchronizedMap(new IdentityHashMap<>(16)); |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { |
|
|
|
public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { |
|
|
|
Method method = invocation.getMethod(); |
|
|
|
Method method = invocation.getMethod(); |
|
|
|
Object target = invocation.getThis(); |
|
|
|
Object instance = invocation.getThis(); |
|
|
|
Class<?> targetClass = (target != null ? target.getClass() : method.getDeclaringClass()); |
|
|
|
Class<?> targetClass = (instance != null ? instance.getClass() : method.getDeclaringClass()); |
|
|
|
if (target == null && invocation instanceof ProxyMethodInvocation methodInvocation) { |
|
|
|
if (invocation instanceof ProxyMethodInvocation methodInvocation) { |
|
|
|
// Support concurrency throttling for AOP proxy without a target
|
|
|
|
// Apply concurrency throttling at the AOP proxy level (independent of target instance)
|
|
|
|
target = methodInvocation.getProxy(); |
|
|
|
instance = methodInvocation.getProxy(); |
|
|
|
} |
|
|
|
} |
|
|
|
Assert.state(target != null, "Target must not be null"); |
|
|
|
Assert.state(instance != null, "Unique instance required - use a ProxyMethodInvocation"); |
|
|
|
|
|
|
|
|
|
|
|
// Build unique ConcurrencyThrottleCache instance per target object
|
|
|
|
// Build unique ConcurrencyThrottleHolder instance per target object
|
|
|
|
ConcurrencyThrottleCache cache = this.cachePerInstance.computeIfAbsent(target, |
|
|
|
ConcurrencyThrottleHolder holder = this.holderPerInstance.computeIfAbsent(instance, |
|
|
|
k -> new ConcurrencyThrottleCache()); |
|
|
|
k -> new ConcurrencyThrottleHolder()); |
|
|
|
|
|
|
|
|
|
|
|
// Determine method-specific interceptor instance with isolated concurrency count
|
|
|
|
// Determine method-specific interceptor instance with isolated concurrency count
|
|
|
|
MethodInterceptor interceptor = cache.methodInterceptors.get(method); |
|
|
|
MethodInterceptor interceptor = holder.methodInterceptors.get(method); |
|
|
|
if (interceptor == null) { |
|
|
|
if (interceptor == null) { |
|
|
|
synchronized (cache) { |
|
|
|
synchronized (holder) { |
|
|
|
interceptor = cache.methodInterceptors.get(method); |
|
|
|
interceptor = holder.methodInterceptors.get(method); |
|
|
|
if (interceptor == null) { |
|
|
|
if (interceptor == null) { |
|
|
|
boolean perMethod = false; |
|
|
|
boolean perMethod = false; |
|
|
|
ConcurrencyLimit annotation = AnnotatedElementUtils.getMergedAnnotation(method, ConcurrencyLimit.class); |
|
|
|
ConcurrencyLimit annotation = AnnotatedElementUtils.getMergedAnnotation(method, ConcurrencyLimit.class); |
|
|
|
@ -104,7 +104,7 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA |
|
|
|
perMethod = true; |
|
|
|
perMethod = true; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
interceptor = cache.classInterceptor; |
|
|
|
interceptor = holder.classInterceptor; |
|
|
|
if (interceptor == null) { |
|
|
|
if (interceptor == null) { |
|
|
|
annotation = AnnotatedElementUtils.getMergedAnnotation(targetClass, ConcurrencyLimit.class); |
|
|
|
annotation = AnnotatedElementUtils.getMergedAnnotation(targetClass, ConcurrencyLimit.class); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -117,10 +117,10 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA |
|
|
|
} |
|
|
|
} |
|
|
|
interceptor = new ConcurrencyThrottleInterceptor(concurrencyLimit); |
|
|
|
interceptor = new ConcurrencyThrottleInterceptor(concurrencyLimit); |
|
|
|
if (!perMethod) { |
|
|
|
if (!perMethod) { |
|
|
|
cache.classInterceptor = interceptor; |
|
|
|
holder.classInterceptor = interceptor; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
cache.methodInterceptors.put(method, interceptor); |
|
|
|
holder.methodInterceptors.put(method, interceptor); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -141,7 +141,7 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static class ConcurrencyThrottleCache { |
|
|
|
private static class ConcurrencyThrottleHolder { |
|
|
|
|
|
|
|
|
|
|
|
final Map<Method, MethodInterceptor> methodInterceptors = new ConcurrentHashMap<>(); |
|
|
|
final Map<Method, MethodInterceptor> methodInterceptors = new ConcurrentHashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
|