Skip to content

Commit 5f695a4

Browse files
committed
Cache Class.getDeclaredMethods() results and avoid pattern matching in isCglibRenamedMethod as far as possible
Issue: SPR-11882 Issue: SPR-11894
1 parent 74c878e commit 5f695a4

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

spring-core/src/main/java/org/springframework/util/ReflectionUtils.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.ArrayList;
2727
import java.util.Arrays;
2828
import java.util.List;
29+
import java.util.Map;
2930
import java.util.regex.Pattern;
3031

3132
/**
@@ -44,11 +45,23 @@
4445
*/
4546
public abstract class ReflectionUtils {
4647

48+
/**
49+
* Naming prefix for CGLIB-renamed methods.
50+
* @see #isCglibRenamedMethod
51+
*/
52+
private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$";
53+
4754
/**
4855
* Pattern for detecting CGLIB-renamed methods.
4956
* @see #isCglibRenamedMethod
5057
*/
51-
private static final Pattern CGLIB_RENAMED_METHOD_PATTERN = Pattern.compile("CGLIB\\$(.+)\\$\\d+");
58+
private static final Pattern CGLIB_RENAMED_METHOD_PATTERN = Pattern.compile("(.+)\\$\\d+");
59+
60+
/**
61+
* Cache for {@link Class#getDeclaredMethods()}, allowing for fast resolution.
62+
*/
63+
private static final Map<Class<?>, Method[]> declaredMethodsCache =
64+
new ConcurrentReferenceHashMap<Class<?>, Method[]>(256);
5265

5366

5467
/**
@@ -156,7 +169,7 @@ public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTy
156169
Assert.notNull(name, "Method name must not be null");
157170
Class<?> searchType = clazz;
158171
while (searchType != null) {
159-
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());
172+
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
160173
for (Method method : methods) {
161174
if (name.equals(method.getName()) &&
162175
(paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
@@ -397,7 +410,9 @@ public static boolean isObjectMethod(Method method) {
397410
* @see org.springframework.cglib.proxy.Enhancer#rename
398411
*/
399412
public static boolean isCglibRenamedMethod(Method renamedMethod) {
400-
return CGLIB_RENAMED_METHOD_PATTERN.matcher(renamedMethod.getName()).matches();
413+
String name = renamedMethod.getName();
414+
return (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX) &&
415+
CGLIB_RENAMED_METHOD_PATTERN.matcher(name.substring(CGLIB_RENAMED_METHOD_PREFIX.length())).matches());
401416
}
402417

403418
/**
@@ -424,8 +439,8 @@ public static void makeAccessible(Field field) {
424439
* @see java.lang.reflect.Method#setAccessible
425440
*/
426441
public static void makeAccessible(Method method) {
427-
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
428-
&& !method.isAccessible()) {
442+
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) &&
443+
!method.isAccessible()) {
429444
method.setAccessible(true);
430445
}
431446
}
@@ -439,8 +454,8 @@ public static void makeAccessible(Method method) {
439454
* @see java.lang.reflect.Constructor#setAccessible
440455
*/
441456
public static void makeAccessible(Constructor<?> ctor) {
442-
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
443-
&& !ctor.isAccessible()) {
457+
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) &&
458+
!ctor.isAccessible()) {
444459
ctor.setAccessible(true);
445460
}
446461
}
@@ -471,7 +486,7 @@ public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter
471486
throws IllegalArgumentException {
472487

473488
// Keep backing up the inheritance hierarchy.
474-
Method[] methods = clazz.getDeclaredMethods();
489+
Method[] methods = getDeclaredMethods(clazz);
475490
for (Method method : methods) {
476491
if (mf != null && !mf.matches(method)) {
477492
continue;
@@ -546,6 +561,19 @@ public void doWith(Method method) {
546561
return methods.toArray(new Method[methods.size()]);
547562
}
548563

564+
/**
565+
* This method retrieves {@link Class#getDeclaredMethods()} from a local cache
566+
* in order to avoid the JVM's SecurityManager check and defensive array copying.
567+
*/
568+
private static Method[] getDeclaredMethods(Class<?> clazz) {
569+
Method[] result = declaredMethodsCache.get(clazz);
570+
if (result == null) {
571+
result = clazz.getDeclaredMethods();
572+
declaredMethodsCache.put(clazz, result);
573+
}
574+
return result;
575+
}
576+
549577
/**
550578
* Invoke the given callback on all fields in the target class, going up the
551579
* class hierarchy to get all declared fields.

0 commit comments

Comments
 (0)