Browse Source

Replace ConcurrentReferenceHashMap with synchronized IdentityHashMap

Closes gh-35788
pull/34993/merge
Juergen Hoeller 1 month ago
parent
commit
39d29c8f7e
  1. 40
      spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java

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

@ -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<>();

Loading…
Cancel
Save