From 9038e6894cbf1dc1bf4b80c0ea506f48a7cc8b81 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 24 May 2017 13:14:12 +0200 Subject: [PATCH] DATACMNS-1074 - Cache MethodHandles of default methods after lookup. We now cache method handles after lookup on a best-effort basis to reuse them and prevent subsequent lookups as a MethodHandle lookup is expensive, in particular the lookup uses exceptions as control flow [0] during speculative lookup [1]. [0] http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/5b86f66575b7/src/share/classes/java/lang/invoke/MemberName.java#l978 [1] http://mail.openjdk.java.net/pipermail/core-libs-dev/2016-August/042770.html Original pull request: #221. --- ...efaultMethodInvokingMethodInterceptor.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/projection/DefaultMethodInvokingMethodInterceptor.java b/src/main/java/org/springframework/data/projection/DefaultMethodInvokingMethodInterceptor.java index dc18b6848..fb8bf0465 100644 --- a/src/main/java/org/springframework/data/projection/DefaultMethodInvokingMethodInterceptor.java +++ b/src/main/java/org/springframework/data/projection/DefaultMethodInvokingMethodInterceptor.java @@ -21,11 +21,14 @@ import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.Map; import java.util.Optional; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.ProxyMethodInvocation; +import org.springframework.util.ConcurrentReferenceHashMap; +import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; import org.springframework.util.ReflectionUtils; /** @@ -38,6 +41,7 @@ import org.springframework.util.ReflectionUtils; public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor { private final MethodHandleLookup methodHandleLookup = MethodHandleLookup.getMethodHandleLookup(); + private final Map methodHandleCache = new ConcurrentReferenceHashMap<>(10, ReferenceType.WEAK); /* * (non-Javadoc) @@ -55,7 +59,20 @@ public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor Object[] arguments = invocation.getArguments(); Object proxy = ((ProxyMethodInvocation) invocation).getProxy(); - return methodHandleLookup.lookup(method).bindTo(proxy).invokeWithArguments(arguments); + return getMethodHandle(method).bindTo(proxy).invokeWithArguments(arguments); + } + + private MethodHandle getMethodHandle(Method method) throws Exception { + + MethodHandle handle = methodHandleCache.get(method); + + if (handle == null) { + + handle = methodHandleLookup.lookup(method); + methodHandleCache.put(method, handle); + } + + return handle; } /**