Resolve bridged method when projected PropertyDescriptor resolves to a bridge method.

We now skip synthetic bridge methods when resolving a PropertyDescriptor from a called interface method on the target type and resolve the bridged method.

Closes #3215
This commit is contained in:
Mark Paluch
2024-12-02 08:57:18 +01:00
parent 2f11039ba3
commit 4bdcbd01f1
2 changed files with 51 additions and 4 deletions

View File

@@ -20,8 +20,10 @@ import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -63,7 +65,8 @@ class PropertyAccessingMethodInterceptor implements MethodInterceptor {
PropertyDescriptor descriptor = BeanUtils.findPropertyForMethod(method);
if (descriptor == null) {
throw new IllegalStateException("Invoked method is not a property accessor");
throw new IllegalStateException("Invoked method '%s' is not a property accessor on '%s'"
.formatted(invocation.getMethod(), target.getWrappedClass().getName()));
}
if (!isSetterMethod(method, descriptor)) {
@@ -84,9 +87,14 @@ class PropertyAccessingMethodInterceptor implements MethodInterceptor {
private static Method lookupTargetMethod(MethodInvocation invocation, Class<?> targetType) {
Method method = BeanUtils.findMethod(targetType, invocation.getMethod().getName(),
invocation.getMethod().getParameterTypes());
Method invokedMethod = invocation.getMethod();
Method method = BeanUtils.findMethod(targetType, invokedMethod.getName(), invokedMethod.getParameterTypes());
return method != null ? method : invocation.getMethod();
if (method == null) {
return invokedMethod;
}
return BridgeMethodResolver.findBridgedMethod(method);
}
}

View File

@@ -121,6 +121,19 @@ class PropertyAccessingMethodInterceptorUnitTests {
assertThat(new PropertyAccessingMethodInterceptor(source).invoke(invocation)).isEqualTo(true);
}
@Test // GH-3697
void considersPropertyDescriptorsFromPackageProtectedSuperclass() throws Throwable {
var source = new SomeExposedClass();
source.setFirstname("Walter");
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("getFirstname"));
Object result = new PropertyAccessingMethodInterceptor(source).invoke(invocation);
assertThat(result).isEqualTo(source.getFirstname());
}
static class Source {
String firstname;
@@ -138,4 +151,30 @@ class PropertyAccessingMethodInterceptorUnitTests {
String someGarbage();
}
static class SomeBaseclass {
private String firstname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
}
public static class SomeExposedClass extends SomeBaseclass {
private String lastname;
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
}