Skip to content

Commit 570b098

Browse files
philwebbsnicoll
authored andcommitted
Add public variant getDeclaredMethods method
Add a public variant of `getDeclaredMethods` that defensively copies the cached methods array. This is often more faster and more convenient for users than calling `doWithLocalMethods`. We still retain most of the benefits of the cache, namely fewer security manager calls and not as many `Method` instances being created. Closes spring-projectsgh-22580
1 parent 9c79d15 commit 570b098

File tree

2 files changed

+25
-10
lines changed

2 files changed

+25
-10
lines changed

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ public static Method findMethod(Class<?> clazz, String name, @Nullable Class<?>.
230230
Assert.notNull(name, "Method name must not be null");
231231
Class<?> searchType = clazz;
232232
while (searchType != null) {
233-
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
233+
Method[] methods = searchType.isInterface() ?
234+
searchType.getMethods() :
235+
getDeclaredMethods(searchType, false);
234236
for (Method method : methods) {
235237
if (name.equals(method.getName()) &&
236238
(paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
@@ -308,7 +310,7 @@ public static boolean declaresException(Method method, Class<?> exceptionType) {
308310
* @see #doWithMethods
309311
*/
310312
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
311-
Method[] methods = getDeclaredMethods(clazz);
313+
Method[] methods = getDeclaredMethods(clazz, false);
312314
for (Method method : methods) {
313315
try {
314316
mc.doWith(method);
@@ -345,7 +347,7 @@ public static void doWithMethods(Class<?> clazz, MethodCallback mc) {
345347
*/
346348
public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
347349
// Keep backing up the inheritance hierarchy.
348-
Method[] methods = getDeclaredMethods(clazz);
350+
Method[] methods = getDeclaredMethods(clazz, false);
349351
for (Method method : methods) {
350352
if (mf != null && !mf.matches(method)) {
351353
continue;
@@ -429,16 +431,22 @@ public static Method[] getUniqueDeclaredMethods(Class<?> leafClass, @Nullable Me
429431
}
430432

431433
/**
432-
* This variant retrieves {@link Class#getDeclaredMethods()} from a local cache
433-
* in order to avoid the JVM's SecurityManager check and defensive array copying.
434-
* In addition, it also includes Java 8 default methods from locally implemented
435-
* interfaces, since those are effectively to be treated just like declared methods.
434+
* Variant of {@link Class#getDeclaredMethods()} that uses a local cache in
435+
* order to avoid the JVM's SecurityManager check and new Method instances.
436+
* In addition, it also includes Java 8 default methods from locally
437+
* implemented interfaces, since those are effectively to be treated just
438+
* like declared methods.
436439
* @param clazz the class to introspect
437440
* @return the cached array of methods
438441
* @throws IllegalStateException if introspection fails
442+
* @since 5.2
439443
* @see Class#getDeclaredMethods()
440444
*/
441-
private static Method[] getDeclaredMethods(Class<?> clazz) {
445+
public static Method[] getDeclaredMethods(Class<?> clazz) {
446+
return getDeclaredMethods(clazz, true);
447+
}
448+
449+
private static Method[] getDeclaredMethods(Class<?> clazz, boolean defensive) {
442450
Assert.notNull(clazz, "Class must not be null");
443451
Method[] result = declaredMethodsCache.get(clazz);
444452
if (result == null) {
@@ -464,7 +472,7 @@ private static Method[] getDeclaredMethods(Class<?> clazz) {
464472
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
465473
}
466474
}
467-
return result;
475+
return (result.length == 0 || !defensive) ? result : result.clone();
468476
}
469477

470478
@Nullable

spring-core/src/test/java/org/springframework/util/ReflectionUtilsTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -360,6 +360,13 @@ void m95() { } void m96() { } void m97() { } void m98() { } void m99() { }
360360
assertThat(totalMs, Matchers.lessThan(10L));
361361
}
362362

363+
@Test
364+
public void getDecalredMethodsReturnsCopy() {
365+
Method[] m1 = ReflectionUtils.getDeclaredMethods(A.class);
366+
Method[] m2 = ReflectionUtils.getDeclaredMethods(A.class);
367+
assertThat(m1, not(sameInstance(m2)));
368+
}
369+
363370
private static class ListSavingMethodCallback implements ReflectionUtils.MethodCallback {
364371

365372
private List<String> methodNames = new LinkedList<>();

0 commit comments

Comments
 (0)