Relocate findPublicDeclaringClass() to CodeFlow
This commit moves findPublicDeclaringClass() from ReflectionHelper to CodeFlow, since findPublicDeclaringClass() is only used for bytecode generation and therefore not for reflection-based invocations.
This commit is contained in:
parent
c4e0f96ef7
commit
38c831f15f
|
@ -18,29 +18,43 @@ package org.springframework.expression.spel;
|
|||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.asm.ClassWriter;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Manages the class being generated by the compilation process.
|
||||
*
|
||||
* <p>Records intermediate compilation state as the bytecode is generated.
|
||||
* Also includes various bytecode generation helper functions.
|
||||
*
|
||||
* <p>Also includes various bytecode generation helper functions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public class CodeFlow implements Opcodes {
|
||||
|
||||
/**
|
||||
* Cache for equivalent methods in a public declaring class in the type
|
||||
* hierarchy of the method's declaring class.
|
||||
* @since 6.2
|
||||
*/
|
||||
private static final Map<Method, Class<?>> publicDeclaringClassCache = new ConcurrentReferenceHashMap<>(256);
|
||||
|
||||
|
||||
/**
|
||||
* Name of the class being generated. Typically used when generating code
|
||||
* that accesses freshly generated fields on the generated type.
|
||||
|
@ -395,6 +409,65 @@ public class CodeFlow implements Opcodes {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first public class or interface in the method's class hierarchy
|
||||
* that declares the supplied method.
|
||||
* <p>Sometimes the reflective method discovery logic finds a suitable method
|
||||
* that can easily be called via reflection but cannot be called from generated
|
||||
* code when compiling the expression because of visibility restrictions. For
|
||||
* example, if a non-public class overrides {@code toString()}, this method
|
||||
* will traverse up the type hierarchy to find the first public type that
|
||||
* declares the method (if there is one). For {@code toString()}, it may
|
||||
* traverse as far as {@link Object}.
|
||||
* @param method the method to process
|
||||
* @return the public class or interface that declares the method, or
|
||||
* {@code null} if no such public type could be found
|
||||
* @since 6.2
|
||||
*/
|
||||
@Nullable
|
||||
public static Class<?> findPublicDeclaringClass(Method method) {
|
||||
return publicDeclaringClassCache.computeIfAbsent(method, key -> {
|
||||
// If the method is already defined in a public type, return that type.
|
||||
if (Modifier.isPublic(key.getDeclaringClass().getModifiers())) {
|
||||
return key.getDeclaringClass();
|
||||
}
|
||||
Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(key, null);
|
||||
// If we found an interface method whose type is public, return the interface type.
|
||||
if (!interfaceMethod.equals(key)) {
|
||||
if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) {
|
||||
return interfaceMethod.getDeclaringClass();
|
||||
}
|
||||
}
|
||||
// Attempt to search the type hierarchy.
|
||||
Class<?> superclass = key.getDeclaringClass().getSuperclass();
|
||||
if (superclass != null) {
|
||||
return findPublicDeclaringClass(superclass, key.getName(), key.getParameterTypes());
|
||||
}
|
||||
// Otherwise, no public declaring class found.
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Class<?> findPublicDeclaringClass(
|
||||
Class<?> declaringClass, String methodName, Class<?>[] parameterTypes) {
|
||||
|
||||
if (Modifier.isPublic(declaringClass.getModifiers())) {
|
||||
try {
|
||||
declaringClass.getDeclaredMethod(methodName, parameterTypes);
|
||||
return declaringClass;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Continue below...
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> superclass = declaringClass.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return findPublicDeclaringClass(superclass, methodName, parameterTypes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the JVM signature descriptor for a method. This consists of the descriptors
|
||||
|
|
|
@ -21,9 +21,7 @@ import java.lang.invoke.MethodType;
|
|||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
@ -36,7 +34,6 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
|
@ -50,14 +47,6 @@ import org.springframework.util.MethodInvoker;
|
|||
*/
|
||||
public abstract class ReflectionHelper {
|
||||
|
||||
/**
|
||||
* Cache for equivalent methods in a public declaring class in the type
|
||||
* hierarchy of the method's declaring class.
|
||||
* @since 6.2
|
||||
*/
|
||||
private static final Map<Method, Class<?>> publicDeclaringClassCache = new ConcurrentReferenceHashMap<>(256);
|
||||
|
||||
|
||||
/**
|
||||
* Compare argument arrays and return information about whether they match.
|
||||
* <p>A supplied type converter and conversionAllowed flag allow for matches to take
|
||||
|
@ -499,66 +488,6 @@ public abstract class ReflectionHelper {
|
|||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first public class or interface in the method's class hierarchy
|
||||
* that declares the supplied method.
|
||||
* <p>Sometimes the reflective method discovery logic finds a suitable method
|
||||
* that can easily be called via reflection but cannot be called from generated
|
||||
* code when compiling the expression because of visibility restrictions. For
|
||||
* example, if a non-public class overrides {@code toString()}, this method
|
||||
* will traverse up the type hierarchy to find the first public type that
|
||||
* declares the method (if there is one). For {@code toString()}, it may
|
||||
* traverse as far as {@link Object}.
|
||||
* @param method the method to process
|
||||
* @return the public class or interface that declares the method, or
|
||||
* {@code null} if no such public type could be found
|
||||
* @since 6.2
|
||||
*/
|
||||
@Nullable
|
||||
public static Class<?> findPublicDeclaringClass(Method method) {
|
||||
return publicDeclaringClassCache.computeIfAbsent(method, key -> {
|
||||
// If the method is already defined in a public type, return that type.
|
||||
if (Modifier.isPublic(key.getDeclaringClass().getModifiers())) {
|
||||
return key.getDeclaringClass();
|
||||
}
|
||||
Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(key, null);
|
||||
// If we found an interface method whose type is public, return the interface type.
|
||||
if (!interfaceMethod.equals(key)) {
|
||||
if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) {
|
||||
return interfaceMethod.getDeclaringClass();
|
||||
}
|
||||
}
|
||||
// Attempt to search the type hierarchy.
|
||||
Class<?> superclass = key.getDeclaringClass().getSuperclass();
|
||||
if (superclass != null) {
|
||||
return findPublicDeclaringClass(superclass, key.getName(), key.getParameterTypes());
|
||||
}
|
||||
// Otherwise, no public declaring class found.
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Class<?> findPublicDeclaringClass(
|
||||
Class<?> declaringClass, String methodName, Class<?>[] parameterTypes) {
|
||||
|
||||
if (Modifier.isPublic(declaringClass.getModifiers())) {
|
||||
try {
|
||||
declaringClass.getDeclaredMethod(methodName, parameterTypes);
|
||||
return declaringClass;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Continue below...
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> superclass = declaringClass.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return findPublicDeclaringClass(superclass, methodName, parameterTypes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Arguments match kinds.
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.springframework.expression.AccessException;
|
|||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
@ -93,7 +94,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
|
|||
/**
|
||||
* Find a public class or interface in the method's class hierarchy that
|
||||
* declares the {@linkplain #getMethod() original method}.
|
||||
* <p>See {@link ReflectionHelper#findPublicDeclaringClass(Method)} for
|
||||
* <p>See {@link CodeFlow#findPublicDeclaringClass(Method)} for
|
||||
* details.
|
||||
* @return the public class or interface that declares the method, or
|
||||
* {@code null} if no such public type could be found
|
||||
|
@ -101,7 +102,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
|
|||
@Nullable
|
||||
public Class<?> getPublicDeclaringClass() {
|
||||
if (!this.computedPublicDeclaringClass) {
|
||||
this.publicDeclaringClass = ReflectionHelper.findPublicDeclaringClass(this.originalMethod);
|
||||
this.publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.originalMethod);
|
||||
this.computedPublicDeclaringClass = true;
|
||||
}
|
||||
return this.publicDeclaringClass;
|
||||
|
|
|
@ -698,7 +698,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
return true;
|
||||
}
|
||||
if (this.originalMethod != null) {
|
||||
return (ReflectionHelper.findPublicDeclaringClass(this.originalMethod) != null);
|
||||
return (CodeFlow.findPublicDeclaringClass(this.originalMethod) != null);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -717,7 +717,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
|
||||
Class<?> publicDeclaringClass = this.member.getDeclaringClass();
|
||||
if (!Modifier.isPublic(publicDeclaringClass.getModifiers()) && this.originalMethod != null) {
|
||||
publicDeclaringClass = ReflectionHelper.findPublicDeclaringClass(this.originalMethod);
|
||||
publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.originalMethod);
|
||||
}
|
||||
Assert.state(publicDeclaringClass != null && Modifier.isPublic(publicDeclaringClass.getModifiers()),
|
||||
() -> "Failed to find public declaring class for: " +
|
||||
|
|
Loading…
Reference in New Issue