BridgeMethodResolver properly resolves interface hierarchies
Issue: SPR-16103
This commit is contained in:
@@ -138,43 +138,13 @@ public abstract class BridgeMethodResolver {
|
||||
return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the generic {@link Method} declaration whose erased signature
|
||||
* matches that of the supplied bridge method.
|
||||
* @throws IllegalStateException if the generic declaration cannot be found
|
||||
*/
|
||||
@Nullable
|
||||
private static Method findGenericDeclaration(Method bridgeMethod) {
|
||||
// Search parent types for method that has same signature as bridge.
|
||||
Class<?> superclass = bridgeMethod.getDeclaringClass().getSuperclass();
|
||||
while (superclass != null && Object.class != superclass) {
|
||||
Method method = searchForMatch(superclass, bridgeMethod);
|
||||
if (method != null && !method.isBridge()) {
|
||||
return method;
|
||||
}
|
||||
superclass = superclass.getSuperclass();
|
||||
}
|
||||
|
||||
// Search interfaces.
|
||||
Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
|
||||
for (Class<?> ifc : interfaces) {
|
||||
Method method = searchForMatch(ifc, bridgeMethod);
|
||||
if (method != null && !method.isBridge()) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the {@link Type} signature of both the supplied
|
||||
* {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
|
||||
* are equal after resolving all types against the declaringType, otherwise
|
||||
* returns {@code false}.
|
||||
*/
|
||||
private static boolean isResolvedTypeMatch(
|
||||
Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
|
||||
private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
|
||||
Type[] genericParameters = genericMethod.getGenericParameterTypes();
|
||||
Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
|
||||
if (genericParameters.length != candidateParameters.length) {
|
||||
@@ -197,6 +167,41 @@ public abstract class BridgeMethodResolver {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the generic {@link Method} declaration whose erased signature
|
||||
* matches that of the supplied bridge method.
|
||||
* @throws IllegalStateException if the generic declaration cannot be found
|
||||
*/
|
||||
@Nullable
|
||||
private static Method findGenericDeclaration(Method bridgeMethod) {
|
||||
// Search parent types for method that has same signature as bridge.
|
||||
Class<?> superclass = bridgeMethod.getDeclaringClass().getSuperclass();
|
||||
while (superclass != null && Object.class != superclass) {
|
||||
Method method = searchForMatch(superclass, bridgeMethod);
|
||||
if (method != null && !method.isBridge()) {
|
||||
return method;
|
||||
}
|
||||
superclass = superclass.getSuperclass();
|
||||
}
|
||||
|
||||
Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
|
||||
return searchInterfaces(interfaces, bridgeMethod);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Method searchInterfaces(Class<?>[] interfaces, Method bridgeMethod) {
|
||||
for (Class<?> ifc : interfaces) {
|
||||
Method method = searchForMatch(ifc, bridgeMethod);
|
||||
if (method != null && !method.isBridge()) {
|
||||
return method;
|
||||
}
|
||||
else {
|
||||
return searchInterfaces(ifc.getInterfaces(), bridgeMethod);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the supplied {@link Class} has a declared {@link Method} whose signature matches
|
||||
* that of the supplied {@link Method}, then this matching {@link Method} is returned,
|
||||
@@ -204,7 +209,12 @@ public abstract class BridgeMethodResolver {
|
||||
*/
|
||||
@Nullable
|
||||
private static Method searchForMatch(Class<?> type, Method bridgeMethod) {
|
||||
return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
|
||||
try {
|
||||
return type.getDeclaredMethod(bridgeMethod.getName(), bridgeMethod.getParameterTypes());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -303,8 +303,26 @@ public class BridgeMethodResolverTests {
|
||||
assertEquals("findBy", bridgedMethod.getName());
|
||||
}
|
||||
|
||||
@Test // SPR-16103
|
||||
public void testClassHierarchy() throws Exception {
|
||||
doTestHierarchyResolution(FooClass.class);
|
||||
}
|
||||
|
||||
public static interface Foo<T extends Serializable> {
|
||||
@Test // SPR-16103
|
||||
public void testInterfaceHierarchy() throws Exception {
|
||||
doTestHierarchyResolution(FooInterface.class);
|
||||
}
|
||||
|
||||
private void doTestHierarchyResolution(Class<?> clazz) throws Exception {
|
||||
for (Method method : clazz.getDeclaredMethods()){
|
||||
Method bridged = BridgeMethodResolver.findBridgedMethod(method);
|
||||
Method expected = clazz.getMethod("test", FooEntity.class);
|
||||
assertEquals(expected, bridged);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface Foo<T extends Serializable> {
|
||||
|
||||
void someMethod(T theArg, Object otherArg);
|
||||
|
||||
@@ -355,7 +373,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface Adder<T> {
|
||||
public interface Adder<T> {
|
||||
|
||||
void add(T item);
|
||||
}
|
||||
@@ -404,7 +422,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface Boo<E, T extends Serializable> {
|
||||
public interface Boo<E, T extends Serializable> {
|
||||
|
||||
void foo(E e);
|
||||
|
||||
@@ -426,17 +444,15 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface Settings {
|
||||
|
||||
public interface Settings {
|
||||
}
|
||||
|
||||
|
||||
public static interface ConcreteSettings extends Settings {
|
||||
|
||||
public interface ConcreteSettings extends Settings {
|
||||
}
|
||||
|
||||
|
||||
public static interface Dao<T, S> {
|
||||
public interface Dao<T, S> {
|
||||
|
||||
T load();
|
||||
|
||||
@@ -444,15 +460,14 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface SettingsDao<T extends Settings, S> extends Dao<T, S> {
|
||||
public interface SettingsDao<T extends Settings, S> extends Dao<T, S> {
|
||||
|
||||
@Override
|
||||
T load();
|
||||
}
|
||||
|
||||
|
||||
public static interface ConcreteSettingsDao extends
|
||||
SettingsDao<ConcreteSettings, String> {
|
||||
public interface ConcreteSettingsDao extends SettingsDao<ConcreteSettings, String> {
|
||||
|
||||
@Override
|
||||
String loadFromParent();
|
||||
@@ -470,7 +485,7 @@ public class BridgeMethodResolverTests {
|
||||
this.otherObject = otherObject;
|
||||
}
|
||||
|
||||
//@Transactional(readOnly = true)
|
||||
// @Transactional(readOnly = true)
|
||||
@Override
|
||||
public S loadFromParent() {
|
||||
return otherObject;
|
||||
@@ -485,7 +500,7 @@ public class BridgeMethodResolverTests {
|
||||
super(object, "From Parent");
|
||||
}
|
||||
|
||||
//@Transactional(readOnly = true)
|
||||
// @Transactional(readOnly = true)
|
||||
@Override
|
||||
public ConcreteSettings load() {
|
||||
return super.object;
|
||||
@@ -493,7 +508,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface Bounded<E> {
|
||||
public interface Bounded<E> {
|
||||
|
||||
boolean boundedOperation(E e);
|
||||
}
|
||||
@@ -517,7 +532,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface GenericParameter<T> {
|
||||
public interface GenericParameter<T> {
|
||||
|
||||
T getFor(Class<T> cls);
|
||||
}
|
||||
@@ -656,7 +671,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface Event {
|
||||
public interface Event {
|
||||
|
||||
int getPriority();
|
||||
}
|
||||
@@ -686,24 +701,19 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface UserInitiatedEvent {
|
||||
|
||||
//public Session getInitiatorSession();
|
||||
public interface UserInitiatedEvent {
|
||||
}
|
||||
|
||||
|
||||
public static abstract class BaseUserInitiatedEvent extends GenericEvent implements
|
||||
UserInitiatedEvent {
|
||||
|
||||
public static abstract class BaseUserInitiatedEvent extends GenericEvent implements UserInitiatedEvent {
|
||||
}
|
||||
|
||||
|
||||
public static class MessageEvent extends BaseUserInitiatedEvent {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static interface Channel<E extends Event> {
|
||||
public interface Channel<E extends Event> {
|
||||
|
||||
void send(E event);
|
||||
|
||||
@@ -713,29 +723,27 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface Broadcaster {
|
||||
public interface Broadcaster {
|
||||
}
|
||||
|
||||
|
||||
public static interface EventBroadcaster extends Broadcaster {
|
||||
public interface EventBroadcaster extends Broadcaster {
|
||||
|
||||
public void subscribe();
|
||||
void subscribe();
|
||||
|
||||
public void unsubscribe();
|
||||
void unsubscribe();
|
||||
|
||||
public void setChannel(Channel<?> channel);
|
||||
void setChannel(Channel<?> channel);
|
||||
}
|
||||
|
||||
|
||||
public static class GenericBroadcasterImpl implements Broadcaster {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "unused", "unchecked" })
|
||||
public static abstract class GenericEventBroadcasterImpl<T extends Event> extends
|
||||
GenericBroadcasterImpl
|
||||
implements EventBroadcaster {
|
||||
public static abstract class GenericEventBroadcasterImpl<T extends Event>
|
||||
extends GenericBroadcasterImpl implements EventBroadcaster {
|
||||
|
||||
private Class<T>[] subscribingEvents;
|
||||
|
||||
@@ -761,27 +769,24 @@ public class BridgeMethodResolverTests {
|
||||
|
||||
@Override
|
||||
public void subscribe() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
|
||||
}
|
||||
|
||||
public GenericEventBroadcasterImpl(Class<? extends T>... events) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static interface Receiver<E extends Event> {
|
||||
public interface Receiver<E extends Event> {
|
||||
|
||||
void receive(E event);
|
||||
}
|
||||
|
||||
|
||||
public static interface MessageBroadcaster extends Receiver<MessageEvent> {
|
||||
public interface MessageBroadcaster extends Receiver<MessageEvent> {
|
||||
|
||||
}
|
||||
|
||||
@@ -835,7 +840,7 @@ public class BridgeMethodResolverTests {
|
||||
// SPR-2454 Test Classes
|
||||
//-----------------------------
|
||||
|
||||
public static interface SimpleGenericRepository<T> {
|
||||
public interface SimpleGenericRepository<T> {
|
||||
|
||||
public Class<T> getPersistentClass();
|
||||
|
||||
@@ -851,7 +856,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface RepositoryRegistry {
|
||||
public interface RepositoryRegistry {
|
||||
|
||||
<T> SimpleGenericRepository<T> getFor(Class<T> entityType);
|
||||
}
|
||||
@@ -883,7 +888,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface ConvenientGenericRepository<T, ID extends Serializable>
|
||||
public interface ConvenientGenericRepository<T, ID extends Serializable>
|
||||
extends SimpleGenericRepository<T> {
|
||||
|
||||
T findById(ID id, boolean lock);
|
||||
@@ -977,7 +982,7 @@ public class BridgeMethodResolverTests {
|
||||
// SPR-2603 classes
|
||||
//-------------------
|
||||
|
||||
public static interface Homer<E> {
|
||||
public interface Homer<E> {
|
||||
|
||||
void foo(E e);
|
||||
}
|
||||
@@ -1002,18 +1007,17 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface GenericDao<T> {
|
||||
public interface GenericDao<T> {
|
||||
|
||||
public void saveOrUpdate(T t);
|
||||
void saveOrUpdate(T t);
|
||||
}
|
||||
|
||||
|
||||
public static interface ConvenienceGenericDao<T> extends GenericDao<T> {
|
||||
public interface ConvenienceGenericDao<T> extends GenericDao<T> {
|
||||
}
|
||||
|
||||
|
||||
public static class GenericSqlMapDao<T extends Serializable> implements
|
||||
ConvenienceGenericDao<T> {
|
||||
public static class GenericSqlMapDao<T extends Serializable> implements ConvenienceGenericDao<T> {
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(T t) {
|
||||
@@ -1022,8 +1026,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static class GenericSqlMapIntegerDao<T extends Number> extends
|
||||
GenericSqlMapDao<T> {
|
||||
public static class GenericSqlMapIntegerDao<T extends Number> extends GenericSqlMapDao<T> {
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(T t) {
|
||||
@@ -1039,12 +1042,12 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface UserDao {
|
||||
public interface UserDao {
|
||||
|
||||
//@Transactional
|
||||
// @Transactional
|
||||
void save(User user);
|
||||
|
||||
//@Transactional
|
||||
// @Transactional
|
||||
void save(Permission perm);
|
||||
}
|
||||
|
||||
@@ -1071,8 +1074,9 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface DaoInterface<T, P> {
|
||||
T get(P id);
|
||||
public interface DaoInterface<T, P> {
|
||||
|
||||
T get(P id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1125,13 +1129,13 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface MegaReceiver<E extends MegaEvent> {
|
||||
public interface MegaReceiver<E extends MegaEvent> {
|
||||
|
||||
void receive(E event);
|
||||
}
|
||||
|
||||
|
||||
public static interface MegaMessageProducer extends MegaReceiver<MegaMessageEvent> {
|
||||
public interface MegaMessageProducer extends MegaReceiver<MegaMessageEvent> {
|
||||
}
|
||||
|
||||
|
||||
@@ -1169,7 +1173,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface IGenericInterface<D extends DomainObjectSuper> {
|
||||
public interface IGenericInterface<D extends DomainObjectSuper> {
|
||||
|
||||
<T> void doSomething(final D domainObject, final T value);
|
||||
}
|
||||
@@ -1234,7 +1238,7 @@ public class BridgeMethodResolverTests {
|
||||
// SPR-3534 classes
|
||||
//-------------------
|
||||
|
||||
public static interface SearchProvider<RETURN_TYPE, CONDITIONS_TYPE> {
|
||||
public interface SearchProvider<RETURN_TYPE, CONDITIONS_TYPE> {
|
||||
|
||||
Collection<RETURN_TYPE> findBy(CONDITIONS_TYPE conditions);
|
||||
}
|
||||
@@ -1244,7 +1248,7 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
public static interface IExternalMessageProvider<S extends ExternalMessage, T extends ExternalMessageSearchConditions<?>>
|
||||
public interface IExternalMessageProvider<S extends ExternalMessage, T extends ExternalMessageSearchConditions<?>>
|
||||
extends SearchProvider<S, T> {
|
||||
}
|
||||
|
||||
@@ -1287,4 +1291,59 @@ public class BridgeMethodResolverTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------
|
||||
// SPR-16103 classes
|
||||
//-------------------
|
||||
|
||||
public static abstract class BaseEntity {
|
||||
|
||||
private String id;
|
||||
}
|
||||
|
||||
public static class FooEntity extends BaseEntity {
|
||||
|
||||
private String name;
|
||||
}
|
||||
|
||||
public static class BaseClass<T> {
|
||||
|
||||
public <S extends T> S test(S T) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntityClass<T extends BaseEntity> extends BaseClass<T> {
|
||||
|
||||
@Override
|
||||
public <S extends T> S test(S T) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FooClass extends EntityClass<FooEntity> {
|
||||
|
||||
@Override
|
||||
public <S extends FooEntity> S test(S T) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface BaseInterface<T> {
|
||||
|
||||
<S extends T> S test(S T);
|
||||
}
|
||||
|
||||
public interface EntityInterface<T extends BaseEntity> extends BaseInterface<T> {
|
||||
|
||||
@Override
|
||||
<S extends T> S test(S T);
|
||||
}
|
||||
|
||||
public interface FooInterface extends EntityInterface<FooEntity> {
|
||||
|
||||
@Override
|
||||
<S extends FooEntity> S test(S T);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user