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:
Chris Beams
2011-05-06 19:07:25 +00:00
parent 2bc3527f76
commit 7b999c676f
5 changed files with 212 additions and 2 deletions

View File

@@ -16,6 +16,9 @@
package org.springframework.util;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -24,7 +27,6 @@ import java.rmi.RemoteException;
import java.util.LinkedList;
import java.util.List;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.TestBean;
@@ -238,6 +240,88 @@ public class ReflectionUtilsTests {
assertNotNull(ReflectionUtils.findMethod(B.class, "getClass"));
}
@Test
public void isCglibRenamedMethod() throws SecurityException, NoSuchMethodException {
@SuppressWarnings("unused")
class C {
public void CGLIB$m1$123() { }
public void CGLIB$m1$0() { }
public void CGLIB$$0() { }
public void CGLIB$m1$() { }
public void CGLIB$m1() { }
public void m1() { }
public void m1$() { }
public void m1$1() { }
}
assertTrue(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("CGLIB$m1$123")));
assertTrue(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("CGLIB$m1$0")));
assertFalse(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("CGLIB$$0")));
assertFalse(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("CGLIB$m1$")));
assertFalse(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("CGLIB$m1")));
assertFalse(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("m1")));
assertFalse(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("m1$")));
assertFalse(ReflectionUtils.isCglibRenamedMethod(C.class.getMethod("m1$1")));
}
@Test
public void getAllDeclaredMethods() throws Exception {
class Foo {
@Override
public String toString() {
return super.toString();
}
}
int toStringMethodCount = 0;
for (Method method : ReflectionUtils.getAllDeclaredMethods(Foo.class)) {
if (method.getName().equals("toString")) {
toStringMethodCount++;
}
}
assertThat(toStringMethodCount, is(2));
}
@Test
public void getUniqueDeclaredMethods() throws Exception {
class Foo {
@Override
public String toString() {
return super.toString();
}
}
int toStringMethodCount = 0;
for (Method method : ReflectionUtils.getUniqueDeclaredMethods(Foo.class)) {
if (method.getName().equals("toString")) {
toStringMethodCount++;
}
}
assertThat(toStringMethodCount, is(1));
}
@Test
public void getUniqueDeclaredMethods_withCovariantReturnType() throws Exception {
class Parent {
public Number m1() {
return new Integer(42);
}
}
class Leaf extends Parent {
@Override
public Integer m1() {
return new Integer(42);
}
}
int m1MethodCount = 0;
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(Leaf.class);
for (Method method : methods) {
if (method.getName().equals("m1")) {
m1MethodCount++;
}
}
assertThat(m1MethodCount, is(1));
assertTrue(ObjectUtils.containsElement(methods, Leaf.class.getMethod("m1")));
assertFalse(ObjectUtils.containsElement(methods, Parent.class.getMethod("m1")));
}
private static class ListSavingMethodCallback implements ReflectionUtils.MethodCallback {