Skip to content

Commit b2c3623

Browse files
committedDec 2, 2024
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
1 parent 00409b3 commit b2c3623

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed
 

‎src/main/java/org/springframework/data/projection/PropertyAccessingMethodInterceptor.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020

2121
import org.aopalliance.intercept.MethodInterceptor;
2222
import org.aopalliance.intercept.MethodInvocation;
23+
2324
import org.springframework.beans.BeanUtils;
2425
import org.springframework.beans.BeanWrapper;
26+
import org.springframework.core.BridgeMethodResolver;
2527
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
2628
import org.springframework.lang.Nullable;
2729
import org.springframework.util.Assert;
@@ -63,7 +65,8 @@ public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) thro
6365
PropertyDescriptor descriptor = BeanUtils.findPropertyForMethod(method);
6466

6567
if (descriptor == null) {
66-
throw new IllegalStateException("Invoked method is not a property accessor");
68+
throw new IllegalStateException("Invoked method '%s' is not a property accessor on '%s'"
69+
.formatted(invocation.getMethod(), target.getWrappedClass().getName()));
6770
}
6871

6972
if (!isSetterMethod(method, descriptor)) {
@@ -84,9 +87,14 @@ private static boolean isSetterMethod(Method method, PropertyDescriptor descript
8487

8588
private static Method lookupTargetMethod(MethodInvocation invocation, Class<?> targetType) {
8689

87-
Method method = BeanUtils.findMethod(targetType, invocation.getMethod().getName(),
88-
invocation.getMethod().getParameterTypes());
90+
Method invokedMethod = invocation.getMethod();
91+
Method method = BeanUtils.findMethod(targetType, invokedMethod.getName(), invokedMethod.getParameterTypes());
92+
93+
if (method == null) {
94+
return invokedMethod;
95+
}
8996

90-
return method != null ? method : invocation.getMethod();
97+
return BridgeMethodResolver.findBridgedMethod(method);
9198
}
99+
92100
}

‎src/test/java/org/springframework/data/projection/PropertyAccessingMethodInterceptorUnitTests.java

+39
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,19 @@ void detectsKotlinPropertiesWithLeadingIsOnTargetType() throws Throwable {
121121
assertThat(new PropertyAccessingMethodInterceptor(source).invoke(invocation)).isEqualTo(true);
122122
}
123123

124+
@Test // GH-3697
125+
void considersPropertyDescriptorsFromPackageProtectedSuperclass() throws Throwable {
126+
127+
var source = new SomeExposedClass();
128+
source.setFirstname("Walter");
129+
130+
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("getFirstname"));
131+
132+
Object result = new PropertyAccessingMethodInterceptor(source).invoke(invocation);
133+
134+
assertThat(result).isEqualTo(source.getFirstname());
135+
}
136+
124137
static class Source {
125138

126139
String firstname;
@@ -138,4 +151,30 @@ interface Projection {
138151

139152
String someGarbage();
140153
}
154+
155+
static class SomeBaseclass {
156+
157+
private String firstname;
158+
159+
public String getFirstname() {
160+
return firstname;
161+
}
162+
163+
public void setFirstname(String firstname) {
164+
this.firstname = firstname;
165+
}
166+
}
167+
168+
public static class SomeExposedClass extends SomeBaseclass {
169+
170+
private String lastname;
171+
172+
public String getLastname() {
173+
return lastname;
174+
}
175+
176+
public void setLastname(String lastname) {
177+
this.lastname = lastname;
178+
}
179+
}
141180
}

0 commit comments

Comments
 (0)
Please sign in to comment.