Browse Source

BridgeMethodResolver properly resolves interface hierarchies

Issue: SPR-16103

(cherry picked from commit 182da15)
pull/1582/head
Juergen Hoeller 8 years ago
parent
commit
6446ffd063
  1. 76
      spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java
  2. 185
      spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java

76
spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -59,6 +59,7 @@ public abstract class BridgeMethodResolver {
if (bridgeMethod == null || !bridgeMethod.isBridge()) { if (bridgeMethod == null || !bridgeMethod.isBridge()) {
return bridgeMethod; return bridgeMethod;
} }
// Gather all methods with matching name and parameter size. // Gather all methods with matching name and parameter size.
List<Method> candidateMethods = new ArrayList<Method>(); List<Method> candidateMethods = new ArrayList<Method>();
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass()); Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
@ -67,10 +68,12 @@ public abstract class BridgeMethodResolver {
candidateMethods.add(candidateMethod); candidateMethods.add(candidateMethod);
} }
} }
// Now perform simple quick check. // Now perform simple quick check.
if (candidateMethods.size() == 1) { if (candidateMethods.size() == 1) {
return candidateMethods.get(0); return candidateMethods.get(0);
} }
// Search for candidate match. // Search for candidate match.
Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod); Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
if (bridgedMethod != null) { if (bridgedMethod != null) {
@ -133,42 +136,13 @@ public abstract class BridgeMethodResolver {
return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass)); 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
*/
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 * Returns {@code true} if the {@link Type} signature of both the supplied
* {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method} * {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
* are equal after resolving all types against the declaringType, otherwise * are equal after resolving all types against the declaringType, otherwise
* returns {@code false}. * returns {@code false}.
*/ */
private static boolean isResolvedTypeMatch( private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
Type[] genericParameters = genericMethod.getGenericParameterTypes(); Type[] genericParameters = genericMethod.getGenericParameterTypes();
Class<?>[] candidateParameters = candidateMethod.getParameterTypes(); Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
if (genericParameters.length != candidateParameters.length) { if (genericParameters.length != candidateParameters.length) {
@ -191,13 +165,51 @@ public abstract class BridgeMethodResolver {
return true; 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
*/
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);
}
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 * 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, * that of the supplied {@link Method}, then this matching {@link Method} is returned,
* otherwise {@code null} is returned. * otherwise {@code null} is returned.
*/ */
private static Method searchForMatch(Class<?> type, Method bridgeMethod) { 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;
}
} }
/** /**

185
spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -344,8 +344,26 @@ public class BridgeMethodResolverTests {
assertEquals("findBy", bridgedMethod.getName()); assertEquals("findBy", bridgedMethod.getName());
} }
@Test // SPR-16103
public void testClassHierarchy() throws Exception {
doTestHierarchyResolution(FooClass.class);
}
@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 static interface Foo<T extends Serializable> { public interface Foo<T extends Serializable> {
void someMethod(T theArg, Object otherArg); void someMethod(T theArg, Object otherArg);
@ -396,7 +414,7 @@ public class BridgeMethodResolverTests {
} }
public static interface Adder<T> { public interface Adder<T> {
void add(T item); void add(T item);
} }
@ -445,7 +463,7 @@ public class BridgeMethodResolverTests {
} }
public static interface Boo<E, T extends Serializable> { public interface Boo<E, T extends Serializable> {
void foo(E e); void foo(E e);
@ -467,17 +485,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(); T load();
@ -485,15 +501,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 @Override
T load(); T load();
} }
public static interface ConcreteSettingsDao extends public interface ConcreteSettingsDao extends SettingsDao<ConcreteSettings, String> {
SettingsDao<ConcreteSettings, String> {
@Override @Override
String loadFromParent(); String loadFromParent();
@ -511,7 +526,7 @@ public class BridgeMethodResolverTests {
this.otherObject = otherObject; this.otherObject = otherObject;
} }
//@Transactional(readOnly = true) // @Transactional(readOnly = true)
@Override @Override
public S loadFromParent() { public S loadFromParent() {
return otherObject; return otherObject;
@ -526,7 +541,7 @@ public class BridgeMethodResolverTests {
super(object, "From Parent"); super(object, "From Parent");
} }
//@Transactional(readOnly = true) // @Transactional(readOnly = true)
@Override @Override
public ConcreteSettings load() { public ConcreteSettings load() {
return super.object; return super.object;
@ -534,7 +549,7 @@ public class BridgeMethodResolverTests {
} }
public static interface Bounded<E> { public interface Bounded<E> {
boolean boundedOperation(E e); boolean boundedOperation(E e);
} }
@ -558,7 +573,7 @@ public class BridgeMethodResolverTests {
} }
public static interface GenericParameter<T> { public interface GenericParameter<T> {
T getFor(Class<T> cls); T getFor(Class<T> cls);
} }
@ -697,7 +712,7 @@ public class BridgeMethodResolverTests {
} }
public static interface Event { public interface Event {
int getPriority(); int getPriority();
} }
@ -727,24 +742,19 @@ public class BridgeMethodResolverTests {
} }
public static interface UserInitiatedEvent { public interface UserInitiatedEvent {
//public Session getInitiatorSession();
} }
public static abstract class BaseUserInitiatedEvent extends GenericEvent implements public static abstract class BaseUserInitiatedEvent extends GenericEvent implements UserInitiatedEvent {
UserInitiatedEvent {
} }
public static class MessageEvent extends BaseUserInitiatedEvent { public static class MessageEvent extends BaseUserInitiatedEvent {
} }
public static interface Channel<E extends Event> { public interface Channel<E extends Event> {
void send(E event); void send(E event);
@ -754,29 +764,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 { public static class GenericBroadcasterImpl implements Broadcaster {
} }
@SuppressWarnings({ "unused", "unchecked" }) @SuppressWarnings({ "unused", "unchecked" })
public static abstract class GenericEventBroadcasterImpl<T extends Event> extends public static abstract class GenericEventBroadcasterImpl<T extends Event>
GenericBroadcasterImpl extends GenericBroadcasterImpl implements EventBroadcaster {
implements EventBroadcaster {
private Class<T>[] subscribingEvents; private Class<T>[] subscribingEvents;
@ -802,27 +810,24 @@ public class BridgeMethodResolverTests {
@Override @Override
public void subscribe() { public void subscribe() {
} }
@Override @Override
public void unsubscribe() { public void unsubscribe() {
} }
public GenericEventBroadcasterImpl(Class<? extends T>... events) { public GenericEventBroadcasterImpl(Class<? extends T>... events) {
} }
} }
public static interface Receiver<E extends Event> { public interface Receiver<E extends Event> {
void receive(E event); void receive(E event);
} }
public static interface MessageBroadcaster extends Receiver<MessageEvent> { public interface MessageBroadcaster extends Receiver<MessageEvent> {
} }
@ -845,7 +850,7 @@ public class BridgeMethodResolverTests {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static class MessageBroadcasterImpl extends public static class MessageBroadcasterImpl extends
GenericEventBroadcasterImpl<MessageEvent> GenericEventBroadcasterImpl<MessageEvent>
implements MessageBroadcaster { implements MessageBroadcaster {
public MessageBroadcasterImpl() { public MessageBroadcasterImpl() {
super(NewMessageEvent.class); super(NewMessageEvent.class);
@ -876,7 +881,7 @@ public class BridgeMethodResolverTests {
// SPR-2454 Test Classes // SPR-2454 Test Classes
//----------------------------- //-----------------------------
public static interface SimpleGenericRepository<T> { public interface SimpleGenericRepository<T> {
public Class<T> getPersistentClass(); public Class<T> getPersistentClass();
@ -892,7 +897,7 @@ public class BridgeMethodResolverTests {
} }
public static interface RepositoryRegistry { public interface RepositoryRegistry {
<T> SimpleGenericRepository<T> getFor(Class<T> entityType); <T> SimpleGenericRepository<T> getFor(Class<T> entityType);
} }
@ -900,7 +905,7 @@ public class BridgeMethodResolverTests {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static class SettableRepositoryRegistry<R extends SimpleGenericRepository<?>> public static class SettableRepositoryRegistry<R extends SimpleGenericRepository<?>>
implements RepositoryRegistry { implements RepositoryRegistry {
protected void injectInto(R rep) { protected void injectInto(R rep) {
} }
@ -924,7 +929,7 @@ public class BridgeMethodResolverTests {
} }
public static interface ConvenientGenericRepository<T, ID extends Serializable> public interface ConvenientGenericRepository<T, ID extends Serializable>
extends SimpleGenericRepository<T> { extends SimpleGenericRepository<T> {
T findById(ID id, boolean lock); T findById(ID id, boolean lock);
@ -938,7 +943,7 @@ public class BridgeMethodResolverTests {
public static class GenericHibernateRepository<T, ID extends Serializable> public static class GenericHibernateRepository<T, ID extends Serializable>
implements ConvenientGenericRepository<T, ID> { implements ConvenientGenericRepository<T, ID> {
/** /**
* @param c Mandatory. The domain class this repository is responsible for. * @param c Mandatory. The domain class this repository is responsible for.
@ -1018,7 +1023,7 @@ public class BridgeMethodResolverTests {
// SPR-2603 classes // SPR-2603 classes
//------------------- //-------------------
public static interface Homer<E> { public interface Homer<E> {
void foo(E e); void foo(E e);
} }
@ -1043,18 +1048,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 public static class GenericSqlMapDao<T extends Serializable> implements ConvenienceGenericDao<T> {
ConvenienceGenericDao<T> {
@Override @Override
public void saveOrUpdate(T t) { public void saveOrUpdate(T t) {
@ -1063,8 +1067,7 @@ public class BridgeMethodResolverTests {
} }
public static class GenericSqlMapIntegerDao<T extends Number> extends public static class GenericSqlMapIntegerDao<T extends Number> extends GenericSqlMapDao<T> {
GenericSqlMapDao<T> {
@Override @Override
public void saveOrUpdate(T t) { public void saveOrUpdate(T t) {
@ -1080,12 +1083,12 @@ public class BridgeMethodResolverTests {
} }
public static interface UserDao { public interface UserDao {
//@Transactional // @Transactional
void save(User user); void save(User user);
//@Transactional // @Transactional
void save(Permission perm); void save(Permission perm);
} }
@ -1112,8 +1115,9 @@ public class BridgeMethodResolverTests {
} }
public static interface DaoInterface<T, P> { public interface DaoInterface<T, P> {
T get(P id);
T get(P id);
} }
@ -1166,13 +1170,13 @@ public class BridgeMethodResolverTests {
} }
public static interface MegaReceiver<E extends MegaEvent> { public interface MegaReceiver<E extends MegaEvent> {
void receive(E event); void receive(E event);
} }
public static interface MegaMessageProducer extends MegaReceiver<MegaMessageEvent> { public interface MegaMessageProducer extends MegaReceiver<MegaMessageEvent> {
} }
@ -1210,7 +1214,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); <T> void doSomething(final D domainObject, final T value);
} }
@ -1275,7 +1279,7 @@ public class BridgeMethodResolverTests {
// SPR-3534 classes // 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); Collection<RETURN_TYPE> findBy(CONDITIONS_TYPE conditions);
} }
@ -1285,7 +1289,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> { extends SearchProvider<S, T> {
} }
@ -1328,4 +1332,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);
}
} }

Loading…
Cancel
Save