Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c6d2758

Browse files
quaffchristophstrobl
authored andcommitted
Throw exception if Kotlin projection requires non-null value but null result present
Closes: #3242 Original Pull Request: #3244 Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
1 parent 9670c87 commit c6d2758

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
lines changed

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@
1616
package org.springframework.data.projection;
1717

1818
import java.lang.reflect.Array;
19+
import java.lang.reflect.Method;
1920
import java.util.Arrays;
2021
import java.util.Collection;
2122
import java.util.Collections;
2223
import java.util.List;
2324
import java.util.Map;
2425
import java.util.Map.Entry;
2526

27+
import kotlin.reflect.KFunction;
2628
import org.aopalliance.intercept.MethodInterceptor;
2729
import org.aopalliance.intercept.MethodInvocation;
2830
import org.springframework.core.CollectionFactory;
31+
import org.springframework.core.KotlinDetector;
2932
import org.springframework.core.convert.ConversionService;
33+
import org.springframework.data.util.KotlinReflectionUtils;
3034
import org.springframework.data.util.NullableWrapper;
3135
import org.springframework.data.util.NullableWrapperConverters;
3236
import org.springframework.data.util.TypeInformation;
@@ -44,6 +48,7 @@
4448
* @author Mark Paluch
4549
* @author Christoph Strobl
4650
* @author Johannes Englmeier
51+
* @author Yanming Zhou
4752
* @since 1.10
4853
*/
4954
class ProjectingMethodInterceptor implements MethodInterceptor {
@@ -64,11 +69,13 @@ class ProjectingMethodInterceptor implements MethodInterceptor {
6469
@Override
6570
public Object invoke(@SuppressWarnings("null") @NonNull MethodInvocation invocation) throws Throwable {
6671

67-
TypeInformation<?> type = TypeInformation.fromReturnTypeOf(invocation.getMethod());
72+
Method method = invocation.getMethod();
73+
TypeInformation<?> type = TypeInformation.fromReturnTypeOf(method);
6874
TypeInformation<?> resultType = type;
6975
TypeInformation<?> typeToReturn = type;
7076

7177
Object result = delegate.invoke(invocation);
78+
7279
boolean applyWrapper = false;
7380

7481
if (NullableWrapperConverters.supports(type.getType())
@@ -83,6 +90,14 @@ public Object invoke(@SuppressWarnings("null") @NonNull MethodInvocation invocat
8390
return conversionService.convert(new NullableWrapper(result), typeToReturn.getType());
8491
}
8592

93+
if (result == null) {
94+
KFunction<?> function = KotlinDetector.isKotlinType(method.getDeclaringClass()) ?
95+
KotlinReflectionUtils.findKotlinFunction(method) : null;
96+
if (function != null && !function.getReturnType().isMarkedNullable()) {
97+
throw new IllegalArgumentException("Kotlin function '%s' requires non-null return value".formatted(method.toString()));
98+
}
99+
}
100+
86101
return result;
87102
}
88103

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* @author Saulo Medeiros de Araujo
4444
* @author Mark Paluch
4545
* @author Christoph Strobl
46+
* @author Yanming Zhou
4647
*/
4748
@ExtendWith(MockitoExtension.class)
4849
class ProjectingMethodInterceptorUnitTests {
@@ -204,6 +205,30 @@ void returnsEnumSet() throws Throwable {
204205
assertThat(collection).containsOnly(HelperEnum.Helpful);
205206
}
206207

208+
@Test
209+
void throwExceptionIfKotlinProjectionRequiresNonNullWithNullResult() throws Throwable {
210+
211+
MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(), interceptor,
212+
conversionService);
213+
214+
when(invocation.getMethod()).thenReturn(Person.class.getMethod("getName"));
215+
when(interceptor.invoke(invocation)).thenReturn(null);
216+
217+
assertThatIllegalArgumentException().isThrownBy(() -> methodInterceptor.invoke(invocation));
218+
}
219+
220+
@Test
221+
void returnsNullIfKotlinProjectionDoesNotRequiresNonNullWithNullResult() throws Throwable {
222+
223+
MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(), interceptor,
224+
conversionService);
225+
226+
when(invocation.getMethod()).thenReturn(Person.class.getMethod("getAge"));
227+
when(interceptor.invoke(invocation)).thenReturn(null);
228+
229+
assertThat(methodInterceptor.invoke(invocation)).isNull();
230+
}
231+
207232
/**
208233
* Mocks the {@link Helper} method of the given name to return the given value.
209234
*
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.projection
18+
19+
/**
20+
* @author Yanming Zhou
21+
*/
22+
interface Person {
23+
val name: String
24+
val age: Int?
25+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /