Cache Class.getDeclaredMethods() results and avoid pattern matching in isCglibRenamedMethod as far as possible
Issue: SPR-11882 Issue: SPR-11894
This commit is contained in:
@@ -26,6 +26,7 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,11 +45,23 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public abstract class ReflectionUtils {
|
public abstract class ReflectionUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Naming prefix for CGLIB-renamed methods.
|
||||||
|
* @see #isCglibRenamedMethod
|
||||||
|
*/
|
||||||
|
private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pattern for detecting CGLIB-renamed methods.
|
* Pattern for detecting CGLIB-renamed methods.
|
||||||
* @see #isCglibRenamedMethod
|
* @see #isCglibRenamedMethod
|
||||||
*/
|
*/
|
||||||
private static final Pattern CGLIB_RENAMED_METHOD_PATTERN = Pattern.compile("CGLIB\\$(.+)\\$\\d+");
|
private static final Pattern CGLIB_RENAMED_METHOD_PATTERN = Pattern.compile("(.+)\\$\\d+");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for {@link Class#getDeclaredMethods()}, allowing for fast resolution.
|
||||||
|
*/
|
||||||
|
private static final Map<Class<?>, Method[]> declaredMethodsCache =
|
||||||
|
new ConcurrentReferenceHashMap<Class<?>, Method[]>(256);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,7 +169,7 @@ public abstract class ReflectionUtils {
|
|||||||
Assert.notNull(name, "Method name must not be null");
|
Assert.notNull(name, "Method name must not be null");
|
||||||
Class<?> searchType = clazz;
|
Class<?> searchType = clazz;
|
||||||
while (searchType != null) {
|
while (searchType != null) {
|
||||||
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());
|
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
if (name.equals(method.getName()) &&
|
if (name.equals(method.getName()) &&
|
||||||
(paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
|
(paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
|
||||||
@@ -397,7 +410,9 @@ public abstract class ReflectionUtils {
|
|||||||
* @see org.springframework.cglib.proxy.Enhancer#rename
|
* @see org.springframework.cglib.proxy.Enhancer#rename
|
||||||
*/
|
*/
|
||||||
public static boolean isCglibRenamedMethod(Method renamedMethod) {
|
public static boolean isCglibRenamedMethod(Method renamedMethod) {
|
||||||
return CGLIB_RENAMED_METHOD_PATTERN.matcher(renamedMethod.getName()).matches();
|
String name = renamedMethod.getName();
|
||||||
|
return (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX) &&
|
||||||
|
CGLIB_RENAMED_METHOD_PATTERN.matcher(name.substring(CGLIB_RENAMED_METHOD_PREFIX.length())).matches());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -424,8 +439,8 @@ public abstract class ReflectionUtils {
|
|||||||
* @see java.lang.reflect.Method#setAccessible
|
* @see java.lang.reflect.Method#setAccessible
|
||||||
*/
|
*/
|
||||||
public static void makeAccessible(Method method) {
|
public static void makeAccessible(Method method) {
|
||||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) &&
|
||||||
&& !method.isAccessible()) {
|
!method.isAccessible()) {
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,8 +454,8 @@ public abstract class ReflectionUtils {
|
|||||||
* @see java.lang.reflect.Constructor#setAccessible
|
* @see java.lang.reflect.Constructor#setAccessible
|
||||||
*/
|
*/
|
||||||
public static void makeAccessible(Constructor<?> ctor) {
|
public static void makeAccessible(Constructor<?> ctor) {
|
||||||
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
|
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) &&
|
||||||
&& !ctor.isAccessible()) {
|
!ctor.isAccessible()) {
|
||||||
ctor.setAccessible(true);
|
ctor.setAccessible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,7 +486,7 @@ public abstract class ReflectionUtils {
|
|||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
// Keep backing up the inheritance hierarchy.
|
// Keep backing up the inheritance hierarchy.
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
Method[] methods = getDeclaredMethods(clazz);
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
if (mf != null && !mf.matches(method)) {
|
if (mf != null && !mf.matches(method)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -546,6 +561,19 @@ public abstract class ReflectionUtils {
|
|||||||
return methods.toArray(new Method[methods.size()]);
|
return methods.toArray(new Method[methods.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method retrieves {@link Class#getDeclaredMethods()} from a local cache
|
||||||
|
* in order to avoid the JVM's SecurityManager check and defensive array copying.
|
||||||
|
*/
|
||||||
|
private static Method[] getDeclaredMethods(Class<?> clazz) {
|
||||||
|
Method[] result = declaredMethodsCache.get(clazz);
|
||||||
|
if (result == null) {
|
||||||
|
result = clazz.getDeclaredMethods();
|
||||||
|
declaredMethodsCache.put(clazz, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke the given callback on all fields in the target class, going up the
|
* Invoke the given callback on all fields in the target class, going up the
|
||||||
* class hierarchy to get all declared fields.
|
* class hierarchy to get all declared fields.
|
||||||
|
|||||||
Reference in New Issue
Block a user