Introduce ReflectionUtils#getUniqueDeclaredMethods
This change is in support of certain polymorphism cases in
@Configuration class inheritance hierarchies. Consider the following
scenario:
@Configuration
public abstract class AbstractConfig {
public abstract Object bean();
}
@Configuration
public class ConcreteConfig {
@Override
@Bean
public BeanPostProcessor bean() { ... }
}
ConcreteConfig overrides AbstractConfig's #bean() method with a
covariant return type, in this case returning an object of type
BeanPostProcessor. It is critically important that the container
is able to detect the return type of ConcreteConfig#bean() in order
to instantiate the BPP at the right point in the lifecycle.
Prior to this change, the container could not do this.
AbstractAutowireCapableBeanFactory#getTypeForFactoryMethod called
ReflectionUtils#getAllDeclaredMethods, which returned Method objects
for both the Object and BeanPostProcessor signatures of the #bean()
method. This confused the implementation sufficiently as not to
choose a type for the factory method at all. This means that the
BPP never gets detected as a BPP.
The new method being introduced here, #getUniqueDeclaredMethods, takes
covariant return types into account, and filters out duplicates,
favoring the most specific / narrow return type.
Additionally, it filters out any CGLIB 'rewritten' methods, which
is important in the case of @Configuration classes, which are
enhanced by CGLIB. See the implementation for further details.
This commit is contained in:
@@ -26,6 +26,7 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Simple utility class for working with the reflection API and handling
|
||||
@@ -38,10 +39,13 @@ import java.util.List;
|
||||
* @author Rod Johnson
|
||||
* @author Costin Leau
|
||||
* @author Sam Brannen
|
||||
* @author Chris Beams
|
||||
* @since 1.2.2
|
||||
*/
|
||||
public abstract class ReflectionUtils {
|
||||
|
||||
private static final Pattern CGLIB_RENAMED_METHOD_PATTERN = Pattern.compile("CGLIB\\$(.+)\\$\\d+");
|
||||
|
||||
/**
|
||||
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
|
||||
* supplied <code>name</code>. Searches all superclasses up to {@link Object}.
|
||||
@@ -379,6 +383,16 @@ public abstract class ReflectionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given method is a CGLIB 'renamed' method, following
|
||||
* the pattern "CGLIB$methodName$0".
|
||||
* @param renamedMethod the method to check
|
||||
* @see net.sf.cglib.proxy.Enhancer#rename
|
||||
*/
|
||||
public static boolean isCglibRenamedMethod(Method renamedMethod) {
|
||||
return CGLIB_RENAMED_METHOD_PATTERN.matcher(renamedMethod.getName()).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given field accessible, explicitly setting it accessible if
|
||||
* necessary. The <code>setAccessible(true)</code> method is only called
|
||||
@@ -487,6 +501,42 @@ public abstract class ReflectionUtils {
|
||||
return methods.toArray(new Method[methods.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique set of declared methods on the leaf class and all superclasses. Leaf
|
||||
* class methods are included first and while traversing the superclass hierarchy any methods found
|
||||
* with signatures matching a method already included are filtered out.
|
||||
*/
|
||||
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException {
|
||||
final List<Method> methods = new ArrayList<Method>(32);
|
||||
doWithMethods(leafClass, new MethodCallback() {
|
||||
public void doWith(Method method) {
|
||||
boolean knownSignature = false;
|
||||
Method methodBeingOverriddenWithCovariantReturnType = null;
|
||||
|
||||
for (Method existingMethod : methods) {
|
||||
if (method.getName().equals(existingMethod.getName()) &&
|
||||
Arrays.equals(method.getParameterTypes(), existingMethod.getParameterTypes())) {
|
||||
// is this a covariant return type situation?
|
||||
if (existingMethod.getReturnType() != method.getReturnType() &&
|
||||
existingMethod.getReturnType().isAssignableFrom(method.getReturnType())) {
|
||||
methodBeingOverriddenWithCovariantReturnType = existingMethod;
|
||||
} else {
|
||||
knownSignature = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (methodBeingOverriddenWithCovariantReturnType != null) {
|
||||
methods.remove(methodBeingOverriddenWithCovariantReturnType);
|
||||
}
|
||||
if (!knownSignature && !isCglibRenamedMethod(method)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
});
|
||||
return methods.toArray(new Method[methods.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given callback on all fields in the target class, going up the
|
||||
* class hierarchy to get all declared fields.
|
||||
|
||||
Reference in New Issue
Block a user