diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index 15ecf85ee14..0427317d179 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/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"); * 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()) { return bridgeMethod; } + // Gather all methods with matching name and parameter size. List candidateMethods = new ArrayList(); Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass()); @@ -67,10 +68,12 @@ public abstract class BridgeMethodResolver { candidateMethods.add(candidateMethod); } } + // Now perform simple quick check. if (candidateMethods.size() == 1) { return candidateMethods.get(0); } + // Search for candidate match. Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod); if (bridgedMethod != null) { @@ -133,42 +136,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 - */ - 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) { @@ -191,13 +165,51 @@ 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 + */ + 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 * that of the supplied {@link Method}, then this matching {@link Method} is returned, * otherwise {@code null} is returned. */ 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; + } } /** diff --git a/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java b/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java index 24b71685b6e..d465840817e 100644 --- a/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java +++ b/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"); * you may not use this file except in compliance with the License. @@ -344,8 +344,26 @@ public class BridgeMethodResolverTests { 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 { + public interface Foo { void someMethod(T theArg, Object otherArg); @@ -396,7 +414,7 @@ public class BridgeMethodResolverTests { } - public static interface Adder { + public interface Adder { void add(T item); } @@ -445,7 +463,7 @@ public class BridgeMethodResolverTests { } - public static interface Boo { + public interface Boo { 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 { + public interface Dao { T load(); @@ -485,15 +501,14 @@ public class BridgeMethodResolverTests { } - public static interface SettingsDao extends Dao { + public interface SettingsDao extends Dao { @Override T load(); } - public static interface ConcreteSettingsDao extends - SettingsDao { + public interface ConcreteSettingsDao extends SettingsDao { @Override String loadFromParent(); @@ -511,7 +526,7 @@ public class BridgeMethodResolverTests { this.otherObject = otherObject; } - //@Transactional(readOnly = true) + // @Transactional(readOnly = true) @Override public S loadFromParent() { return otherObject; @@ -526,7 +541,7 @@ public class BridgeMethodResolverTests { super(object, "From Parent"); } - //@Transactional(readOnly = true) + // @Transactional(readOnly = true) @Override public ConcreteSettings load() { return super.object; @@ -534,7 +549,7 @@ public class BridgeMethodResolverTests { } - public static interface Bounded { + public interface Bounded { boolean boundedOperation(E e); } @@ -558,7 +573,7 @@ public class BridgeMethodResolverTests { } - public static interface GenericParameter { + public interface GenericParameter { T getFor(Class cls); } @@ -697,7 +712,7 @@ public class BridgeMethodResolverTests { } - public static interface Event { + public interface Event { int getPriority(); } @@ -727,24 +742,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 { + public interface Channel { 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 { - } @SuppressWarnings({ "unused", "unchecked" }) - public static abstract class GenericEventBroadcasterImpl extends - GenericBroadcasterImpl - implements EventBroadcaster { + public static abstract class GenericEventBroadcasterImpl + extends GenericBroadcasterImpl implements EventBroadcaster { private Class[] subscribingEvents; @@ -802,27 +810,24 @@ public class BridgeMethodResolverTests { @Override public void subscribe() { - } @Override public void unsubscribe() { - } public GenericEventBroadcasterImpl(Class... events) { - } } - public static interface Receiver { + public interface Receiver { void receive(E event); } - public static interface MessageBroadcaster extends Receiver { + public interface MessageBroadcaster extends Receiver { } @@ -845,7 +850,7 @@ public class BridgeMethodResolverTests { @SuppressWarnings("unchecked") public static class MessageBroadcasterImpl extends GenericEventBroadcasterImpl - implements MessageBroadcaster { + implements MessageBroadcaster { public MessageBroadcasterImpl() { super(NewMessageEvent.class); @@ -876,7 +881,7 @@ public class BridgeMethodResolverTests { // SPR-2454 Test Classes //----------------------------- - public static interface SimpleGenericRepository { + public interface SimpleGenericRepository { public Class getPersistentClass(); @@ -892,7 +897,7 @@ public class BridgeMethodResolverTests { } - public static interface RepositoryRegistry { + public interface RepositoryRegistry { SimpleGenericRepository getFor(Class entityType); } @@ -900,7 +905,7 @@ public class BridgeMethodResolverTests { @SuppressWarnings("unchecked") public static class SettableRepositoryRegistry> - implements RepositoryRegistry { + implements RepositoryRegistry { protected void injectInto(R rep) { } @@ -924,7 +929,7 @@ public class BridgeMethodResolverTests { } - public static interface ConvenientGenericRepository + public interface ConvenientGenericRepository extends SimpleGenericRepository { T findById(ID id, boolean lock); @@ -938,7 +943,7 @@ public class BridgeMethodResolverTests { public static class GenericHibernateRepository - implements ConvenientGenericRepository { + implements ConvenientGenericRepository { /** * @param c Mandatory. The domain class this repository is responsible for. @@ -1018,7 +1023,7 @@ public class BridgeMethodResolverTests { // SPR-2603 classes //------------------- - public static interface Homer { + public interface Homer { void foo(E e); } @@ -1043,18 +1048,17 @@ public class BridgeMethodResolverTests { } - public static interface GenericDao { + public interface GenericDao { - public void saveOrUpdate(T t); + void saveOrUpdate(T t); } - public static interface ConvenienceGenericDao extends GenericDao { + public interface ConvenienceGenericDao extends GenericDao { } - public static class GenericSqlMapDao implements - ConvenienceGenericDao { + public static class GenericSqlMapDao implements ConvenienceGenericDao { @Override public void saveOrUpdate(T t) { @@ -1063,8 +1067,7 @@ public class BridgeMethodResolverTests { } - public static class GenericSqlMapIntegerDao extends - GenericSqlMapDao { + public static class GenericSqlMapIntegerDao extends GenericSqlMapDao { @Override 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); - //@Transactional + // @Transactional void save(Permission perm); } @@ -1112,8 +1115,9 @@ public class BridgeMethodResolverTests { } - public static interface DaoInterface { - T get(P id); + public interface DaoInterface { + + T get(P id); } @@ -1166,13 +1170,13 @@ public class BridgeMethodResolverTests { } - public static interface MegaReceiver { + public interface MegaReceiver { void receive(E event); } - public static interface MegaMessageProducer extends MegaReceiver { + public interface MegaMessageProducer extends MegaReceiver { } @@ -1210,7 +1214,7 @@ public class BridgeMethodResolverTests { } - public static interface IGenericInterface { + public interface IGenericInterface { void doSomething(final D domainObject, final T value); } @@ -1275,7 +1279,7 @@ public class BridgeMethodResolverTests { // SPR-3534 classes //------------------- - public static interface SearchProvider { + public interface SearchProvider { Collection findBy(CONDITIONS_TYPE conditions); } @@ -1285,7 +1289,7 @@ public class BridgeMethodResolverTests { } - public static interface IExternalMessageProvider> + public interface IExternalMessageProvider> extends SearchProvider { } @@ -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 { + + public S test(S T) { + return null; + } + } + + public static class EntityClass extends BaseClass { + + @Override + public S test(S T) { + return null; + } + } + + public static class FooClass extends EntityClass { + + @Override + public S test(S T) { + return null; + } + } + + public interface BaseInterface { + + S test(S T); + } + + public interface EntityInterface extends BaseInterface { + + @Override + S test(S T); + } + + public interface FooInterface extends EntityInterface { + + @Override + S test(S T); + } + }