Skip to content

Commit 38c831f

Browse files
committed
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.
1 parent c4e0f96 commit 38c831f

File tree

4 files changed

+79
-76
lines changed

4 files changed

+79
-76
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,43 @@
1818

1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.Method;
21+
import java.lang.reflect.Modifier;
2122
import java.util.ArrayDeque;
2223
import java.util.ArrayList;
2324
import java.util.Deque;
2425
import java.util.List;
26+
import java.util.Map;
2527

2628
import org.springframework.asm.ClassWriter;
2729
import org.springframework.asm.MethodVisitor;
2830
import org.springframework.asm.Opcodes;
2931
import org.springframework.lang.Nullable;
32+
import org.springframework.util.ClassUtils;
3033
import org.springframework.util.CollectionUtils;
34+
import org.springframework.util.ConcurrentReferenceHashMap;
3135

3236
/**
3337
* Manages the class being generated by the compilation process.
3438
*
3539
* <p>Records intermediate compilation state as the bytecode is generated.
36-
* Also includes various bytecode generation helper functions.
40+
*
41+
* <p>Also includes various bytecode generation helper functions.
3742
*
3843
* @author Andy Clement
3944
* @author Juergen Hoeller
45+
* @author Sam Brannen
4046
* @since 4.1
4147
*/
4248
public class CodeFlow implements Opcodes {
4349

50+
/**
51+
* Cache for equivalent methods in a public declaring class in the type
52+
* hierarchy of the method's declaring class.
53+
* @since 6.2
54+
*/
55+
private static final Map<Method, Class<?>> publicDeclaringClassCache = new ConcurrentReferenceHashMap<>(256);
56+
57+
4458
/**
4559
* Name of the class being generated. Typically used when generating code
4660
* that accesses freshly generated fields on the generated type.
@@ -395,6 +409,65 @@ public static void insertAnyNecessaryTypeConversionBytecodes(MethodVisitor mv, c
395409
}
396410
}
397411

412+
/**
413+
* Find the first public class or interface in the method's class hierarchy
414+
* that declares the supplied method.
415+
* <p>Sometimes the reflective method discovery logic finds a suitable method
416+
* that can easily be called via reflection but cannot be called from generated
417+
* code when compiling the expression because of visibility restrictions. For
418+
* example, if a non-public class overrides {@code toString()}, this method
419+
* will traverse up the type hierarchy to find the first public type that
420+
* declares the method (if there is one). For {@code toString()}, it may
421+
* traverse as far as {@link Object}.
422+
* @param method the method to process
423+
* @return the public class or interface that declares the method, or
424+
* {@code null} if no such public type could be found
425+
* @since 6.2
426+
*/
427+
@Nullable
428+
public static Class<?> findPublicDeclaringClass(Method method) {
429+
return publicDeclaringClassCache.computeIfAbsent(method, key -> {
430+
// If the method is already defined in a public type, return that type.
431+
if (Modifier.isPublic(key.getDeclaringClass().getModifiers())) {
432+
return key.getDeclaringClass();
433+
}
434+
Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(key, null);
435+
// If we found an interface method whose type is public, return the interface type.
436+
if (!interfaceMethod.equals(key)) {
437+
if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) {
438+
return interfaceMethod.getDeclaringClass();
439+
}
440+
}
441+
// Attempt to search the type hierarchy.
442+
Class<?> superclass = key.getDeclaringClass().getSuperclass();
443+
if (superclass != null) {
444+
return findPublicDeclaringClass(superclass, key.getName(), key.getParameterTypes());
445+
}
446+
// Otherwise, no public declaring class found.
447+
return null;
448+
});
449+
}
450+
451+
@Nullable
452+
private static Class<?> findPublicDeclaringClass(
453+
Class<?> declaringClass, String methodName, Class<?>[] parameterTypes) {
454+
455+
if (Modifier.isPublic(declaringClass.getModifiers())) {
456+
try {
457+
declaringClass.getDeclaredMethod(methodName, parameterTypes);
458+
return declaringClass;
459+
}
460+
catch (NoSuchMethodException ex) {
461+
// Continue below...
462+
}
463+
}
464+
465+
Class<?> superclass = declaringClass.getSuperclass();
466+
if (superclass != null) {
467+
return findPublicDeclaringClass(superclass, methodName, parameterTypes);
468+
}
469+
return null;
470+
}
398471

399472
/**
400473
* Create the JVM signature descriptor for a method. This consists of the descriptors

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
import java.lang.reflect.Array;
2222
import java.lang.reflect.Executable;
2323
import java.lang.reflect.Method;
24-
import java.lang.reflect.Modifier;
2524
import java.util.List;
26-
import java.util.Map;
2725
import java.util.Optional;
2826

2927
import org.springframework.core.MethodParameter;
@@ -36,7 +34,6 @@
3634
import org.springframework.util.Assert;
3735
import org.springframework.util.ClassUtils;
3836
import org.springframework.util.CollectionUtils;
39-
import org.springframework.util.ConcurrentReferenceHashMap;
4037
import org.springframework.util.MethodInvoker;
4138

4239
/**
@@ -50,14 +47,6 @@
5047
*/
5148
public abstract class ReflectionHelper {
5249

53-
/**
54-
* Cache for equivalent methods in a public declaring class in the type
55-
* hierarchy of the method's declaring class.
56-
* @since 6.2
57-
*/
58-
private static final Map<Method, Class<?>> publicDeclaringClassCache = new ConcurrentReferenceHashMap<>(256);
59-
60-
6150
/**
6251
* Compare argument arrays and return information about whether they match.
6352
* <p>A supplied type converter and conversionAllowed flag allow for matches to take
@@ -499,66 +488,6 @@ public static Object[] setupArgumentsForVarargsInvocation(Class<?>[] requiredPar
499488
return args;
500489
}
501490

502-
/**
503-
* Find the first public class or interface in the method's class hierarchy
504-
* that declares the supplied method.
505-
* <p>Sometimes the reflective method discovery logic finds a suitable method
506-
* that can easily be called via reflection but cannot be called from generated
507-
* code when compiling the expression because of visibility restrictions. For
508-
* example, if a non-public class overrides {@code toString()}, this method
509-
* will traverse up the type hierarchy to find the first public type that
510-
* declares the method (if there is one). For {@code toString()}, it may
511-
* traverse as far as {@link Object}.
512-
* @param method the method to process
513-
* @return the public class or interface that declares the method, or
514-
* {@code null} if no such public type could be found
515-
* @since 6.2
516-
*/
517-
@Nullable
518-
public static Class<?> findPublicDeclaringClass(Method method) {
519-
return publicDeclaringClassCache.computeIfAbsent(method, key -> {
520-
// If the method is already defined in a public type, return that type.
521-
if (Modifier.isPublic(key.getDeclaringClass().getModifiers())) {
522-
return key.getDeclaringClass();
523-
}
524-
Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(key, null);
525-
// If we found an interface method whose type is public, return the interface type.
526-
if (!interfaceMethod.equals(key)) {
527-
if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) {
528-
return interfaceMethod.getDeclaringClass();
529-
}
530-
}
531-
// Attempt to search the type hierarchy.
532-
Class<?> superclass = key.getDeclaringClass().getSuperclass();
533-
if (superclass != null) {
534-
return findPublicDeclaringClass(superclass, key.getName(), key.getParameterTypes());
535-
}
536-
// Otherwise, no public declaring class found.
537-
return null;
538-
});
539-
}
540-
541-
@Nullable
542-
private static Class<?> findPublicDeclaringClass(
543-
Class<?> declaringClass, String methodName, Class<?>[] parameterTypes) {
544-
545-
if (Modifier.isPublic(declaringClass.getModifiers())) {
546-
try {
547-
declaringClass.getDeclaredMethod(methodName, parameterTypes);
548-
return declaringClass;
549-
}
550-
catch (NoSuchMethodException ex) {
551-
// Continue below...
552-
}
553-
}
554-
555-
Class<?> superclass = declaringClass.getSuperclass();
556-
if (superclass != null) {
557-
return findPublicDeclaringClass(superclass, methodName, parameterTypes);
558-
}
559-
return null;
560-
}
561-
562491

563492
/**
564493
* Arguments match kinds.

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.expression.EvaluationContext;
2525
import org.springframework.expression.MethodExecutor;
2626
import org.springframework.expression.TypedValue;
27+
import org.springframework.expression.spel.CodeFlow;
2728
import org.springframework.lang.Nullable;
2829
import org.springframework.util.ClassUtils;
2930
import org.springframework.util.ReflectionUtils;
@@ -93,15 +94,15 @@ public final Method getMethod() {
9394
/**
9495
* Find a public class or interface in the method's class hierarchy that
9596
* declares the {@linkplain #getMethod() original method}.
96-
* <p>See {@link ReflectionHelper#findPublicDeclaringClass(Method)} for
97+
* <p>See {@link CodeFlow#findPublicDeclaringClass(Method)} for
9798
* details.
9899
* @return the public class or interface that declares the method, or
99100
* {@code null} if no such public type could be found
100101
*/
101102
@Nullable
102103
public Class<?> getPublicDeclaringClass() {
103104
if (!this.computedPublicDeclaringClass) {
104-
this.publicDeclaringClass = ReflectionHelper.findPublicDeclaringClass(this.originalMethod);
105+
this.publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.originalMethod);
105106
this.computedPublicDeclaringClass = true;
106107
}
107108
return this.publicDeclaringClass;

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ public boolean isCompilable() {
698698
return true;
699699
}
700700
if (this.originalMethod != null) {
701-
return (ReflectionHelper.findPublicDeclaringClass(this.originalMethod) != null);
701+
return (CodeFlow.findPublicDeclaringClass(this.originalMethod) != null);
702702
}
703703
return false;
704704
}
@@ -717,7 +717,7 @@ public Class<?> getPropertyType() {
717717
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
718718
Class<?> publicDeclaringClass = this.member.getDeclaringClass();
719719
if (!Modifier.isPublic(publicDeclaringClass.getModifiers()) && this.originalMethod != null) {
720-
publicDeclaringClass = ReflectionHelper.findPublicDeclaringClass(this.originalMethod);
720+
publicDeclaringClass = CodeFlow.findPublicDeclaringClass(this.originalMethod);
721721
}
722722
Assert.state(publicDeclaringClass != null && Modifier.isPublic(publicDeclaringClass.getModifiers()),
723723
() -> "Failed to find public declaring class for: " +

0 commit comments

Comments
 (0)