Yanming Zhou opened SPR-17310 and commented
Persistable <- BaseUser <- User
BaseManager<T extends Persistable> <- BaseUserManager<T extends BaseUser> <- UserManager<User>
BaseManagerImpl<T extends Persistable> <- BaseUserManagerImpl<T extends BaseUser> <- UserManagerImpl<User>
The mostSpecificMethod should return method UserManagerImpl.save(User), but return BaseUserManagerImpl.save(BaseUser) which doesn't annotated, it will cause @annotation
pointcut broken.
Affects: 5.1 GA
Attachments: - DeclaringClassTest.zip (10.66 kB) - spr17310.zip (10.24 kB)
Comment From: spring-projects-issues
Yanming Zhou commented
It seems eclipse compiler bug. I'm not sure it has relation with https://bugs.eclipse.org/bugs/show_bug.cgi?id=495396
Comment From: spring-projects-issues
Yanming Zhou commented
I have reported https://bugs.eclipse.org/bugs/show_bug.cgi?id=539651
Comment From: spring-projects-issues
Yanming Zhou commented
Finally I figure it out, method.getDeclaringClass() return wrong class with eclipse compiler, it cause spring ClassUtils.getMostSpecificMethod() return wrong method.
@Test
public void test() throws Exception {
Class<?> targetClass = UserManagerImpl.class;
Method method = targetClass.getMethod("save", Persistable.class);
assertEquals(targetClass, method.getDeclaringClass()); // eclipse compiler fail
}
Comment From: spring-projects-issues
Yanming Zhou commented
javac create two bridge methods:
public void com.example.UserManagerImpl.save(com.example.Persistable)
public void com.example.UserManagerImpl.save(com.example.BaseUser)
ecj create only one bridge method:
public void com.example.UserManagerImpl.save(com.example.BaseUser)
It seems a corner case that JLS not covered, maybe it's not a bug of eclipse compiler, Juergen Hoeller could you make some changes to keep compatibility with both of them?
Comment From: spring-projects-issues
Yanming Zhou commented
Workaround:
add two lines before return statement
resolvedMethod = BridgeMethodResolver.findBridgedMethod(resolvedMethod);
resolvedMethod = ClassUtils.getMostSpecificMethod(resolvedMethod, specificTargetClass);
https://github.com/spring-projects/spring-framework/blob/master/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java#L198
Comment From: quaff
Minimized unit test, It will fail in eclipse.
import static org.junit.Assert.assertEquals;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.aop.support.AopUtils;
public class AopUtilsTest {
@Test
public void testGetMostSpecificMethod() throws Exception {
String methodName = "feed";
Class<?> targetClass = DogService.class;
Method targetMethod = targetClass.getDeclaredMethod(methodName, Dog.class);
Method originalMethod = AnimalService.class.getDeclaredMethod(methodName, Animal.class);
Method actualMethod = AopUtils.getMostSpecificMethod(originalMethod, targetClass);
assertEquals("Please ensure class compiled with javac not eclipse", targetMethod, actualMethod);
}
public static class Animal {
}
public static abstract class Mammal extends Animal {
}
public static class Dog extends Mammal {
}
public static class AnimalService<T extends Animal> {
public void feed(T obj) {
}
}
public static class MammalService<T extends Mammal> extends AnimalService<T> {
@Override
public void feed(T obj) {
}
}
public static class DogService extends MammalService<Dog> {
@Override
public void feed(Dog obj) {
}
}
}
Comment From: snicoll
Courtesy of @wilkinsona, the test is still failing with the latest Spring Framework 6.1 milestone.
@jhoeller wondering if we could accommodate with this difference between the eclipse and Java compiler?
Comment From: jhoeller
I've addressed this through a new combined BridgeMethodResolver.getMostSpecificMethod(Method, Class)
delegate which does ClassUtils.getMostSpecificMethod
and then resolves the bridge method against the same target class, being able to find a matching signature even if no bridge method has been generated at the same class hierarchy level (as with the Eclipse compiler).