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 2894ff3

Browse files
Move and reuse existing nullability validator.
Original Pull Request: #3244
1 parent c6d2758 commit 2894ff3

File tree

11 files changed

+335
-221
lines changed

11 files changed

+335
-221
lines changed

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,10 @@
2424
import java.util.Map;
2525
import java.util.Map.Entry;
2626

27-
import kotlin.reflect.KFunction;
2827
import org.aopalliance.intercept.MethodInterceptor;
2928
import org.aopalliance.intercept.MethodInvocation;
3029
import org.springframework.core.CollectionFactory;
31-
import org.springframework.core.KotlinDetector;
3230
import org.springframework.core.convert.ConversionService;
33-
import org.springframework.data.util.KotlinReflectionUtils;
3431
import org.springframework.data.util.NullableWrapper;
3532
import org.springframework.data.util.NullableWrapperConverters;
3633
import org.springframework.data.util.TypeInformation;
@@ -90,14 +87,6 @@ public Object invoke(@SuppressWarnings("null") @NonNull MethodInvocation invocat
9087
return conversionService.convert(new NullableWrapper(result), typeToReturn.getType());
9188
}
9289

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-
10190
return result;
10291
}
10392

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.core.convert.support.GenericConversionService;
3232
import org.springframework.data.convert.Jsr310Converters;
3333
import org.springframework.data.util.Lazy;
34+
import org.springframework.data.util.NullabilityMethodInvocationValidator;
3435
import org.springframework.data.util.NullableWrapperConverters;
3536
import org.springframework.lang.Nullable;
3637
import org.springframework.util.Assert;
@@ -66,6 +67,9 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
6667
private final Lazy<DefaultMethodInvokingMethodInterceptor> defaultMethodInvokingMethodInterceptor = Lazy
6768
.of(DefaultMethodInvokingMethodInterceptor::new);
6869

70+
private final Lazy<NullabilityMethodInvocationValidator> nullabilityValidator = Lazy
71+
.of(NullabilityMethodInvocationValidator::new);
72+
6973
/**
7074
* Creates a new {@link ProxyProjectionFactory}.
7175
*/
@@ -119,6 +123,11 @@ public <T> T createProjection(Class<T> projectionType, Object source) {
119123
}
120124

121125
factory.addAdvice(new TargetAwareMethodInterceptor(source.getClass()));
126+
127+
if(NullabilityMethodInvocationValidator.supports(projectionType)) {
128+
factory.addAdvice(nullabilityValidator.get());
129+
}
130+
122131
factory.addAdvice(getMethodInterceptor(source, projectionType));
123132

124133
return (T) factory.getProxy(classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader);

‎src/main/java/org/springframework/data/repository/core/support/MethodInvocationValidator.java

Lines changed: 10 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -15,196 +15,26 @@
1515
*/
1616
package org.springframework.data.repository.core.support;
1717

18-
import java.lang.annotation.ElementType;
19-
import java.lang.reflect.Method;
20-
import java.util.Map;
21-
import java.util.concurrent.ConcurrentHashMap;
22-
23-
import org.aopalliance.intercept.MethodInterceptor;
24-
import org.aopalliance.intercept.MethodInvocation;
25-
import org.springframework.core.DefaultParameterNameDiscoverer;
26-
import org.springframework.core.KotlinDetector;
27-
import org.springframework.core.MethodParameter;
28-
import org.springframework.core.ParameterNameDiscoverer;
2918
import org.springframework.dao.EmptyResultDataAccessException;
30-
import org.springframework.data.util.KotlinReflectionUtils;
31-
import org.springframework.data.util.NullableUtils;
32-
import org.springframework.data.util.ReflectionUtils;
33-
import org.springframework.lang.Nullable;
34-
import org.springframework.util.ClassUtils;
35-
import org.springframework.util.ConcurrentReferenceHashMap;
36-
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
37-
import org.springframework.util.ObjectUtils;
19+
import org.springframework.data.util.NullabilityMethodInvocationValidator;
3820

3921
/**
4022
* Interceptor enforcing required return value and method parameter constraints declared on repository query methods.
4123
* Supports Kotlin nullability markers and JSR-305 Non-null annotations.
4224
*
4325
* @author Mark Paluch
4426
* @author Johannes Englmeier
27+
* @author Christoph Strobl
4528
* @since 2.0
4629
* @see org.springframework.lang.NonNull
47-
* @see ReflectionUtils#isNullable(MethodParameter)
48-
* @see NullableUtils
30+
* @see org.springframework.data.util.ReflectionUtils#isNullable(org.springframework.core.MethodParameter)
31+
* @see org.springframework.data.util.NullableUtils
32+
* @deprecated use {@link NullabilityMethodInvocationValidator} instead.
4933
*/
50-
public class MethodInvocationValidator implements MethodInterceptor {
51-
52-
private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
53-
private final Map<Method, Nullability> nullabilityCache = new ConcurrentHashMap<>(16);
54-
55-
/**
56-
* Returns {@literal true} if the {@code repositoryInterface} is supported by this interceptor.
57-
*
58-
* @param repositoryInterface the interface class.
59-
* @return {@literal true} if the {@code repositoryInterface} is supported by this interceptor.
60-
*/
61-
public static boolean supports(Class<?> repositoryInterface) {
62-
63-
return KotlinDetector.isKotlinPresent() && KotlinReflectionUtils.isSupportedKotlinClass(repositoryInterface)
64-
|| NullableUtils.isNonNull(repositoryInterface, ElementType.METHOD)
65-
|| NullableUtils.isNonNull(repositoryInterface, ElementType.PARAMETER);
66-
}
67-
68-
@Nullable
69-
@Override
70-
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
71-
72-
Method method = invocation.getMethod();
73-
Nullability nullability = nullabilityCache.get(method);
74-
75-
if (nullability == null) {
76-
77-
nullability = Nullability.of(method, discoverer);
78-
nullabilityCache.put(method, nullability);
79-
}
80-
81-
Object[] arguments = invocation.getArguments();
82-
83-
for (int i = 0; i < method.getParameterCount(); i++) {
84-
85-
if (nullability.isNullableParameter(i)) {
86-
continue;
87-
}
88-
89-
if ((arguments.length < i) || (arguments[i] == null)) {
90-
throw new IllegalArgumentException(
91-
String.format("Parameter %s in %s.%s must not be null", nullability.getMethodParameterName(i),
92-
ClassUtils.getShortName(method.getDeclaringClass()), method.getName()));
93-
}
94-
}
95-
96-
Object result = invocation.proceed();
97-
98-
if ((result == null) && !nullability.isNullableReturn()) {
99-
throw new EmptyResultDataAccessException("Result must not be null", 1);
100-
}
101-
102-
return result;
103-
}
104-
105-
static final class Nullability {
106-
107-
private final boolean nullableReturn;
108-
private final boolean[] nullableParameters;
109-
private final MethodParameter[] methodParameters;
110-
111-
private Nullability(boolean nullableReturn, boolean[] nullableParameters, MethodParameter[] methodParameters) {
112-
this.nullableReturn = nullableReturn;
113-
this.nullableParameters = nullableParameters;
114-
this.methodParameters = methodParameters;
115-
}
116-
117-
static Nullability of(Method method, ParameterNameDiscoverer discoverer) {
118-
119-
boolean nullableReturn = isNullableParameter(new MethodParameter(method, -1));
120-
boolean[] nullableParameters = new boolean[method.getParameterCount()];
121-
MethodParameter[] methodParameters = new MethodParameter[method.getParameterCount()];
122-
123-
for (int i = 0; i < method.getParameterCount(); i++) {
124-
125-
MethodParameter parameter = new MethodParameter(method, i);
126-
parameter.initParameterNameDiscovery(discoverer);
127-
nullableParameters[i] = isNullableParameter(parameter);
128-
methodParameters[i] = parameter;
129-
}
130-
131-
return new Nullability(nullableReturn, nullableParameters, methodParameters);
132-
}
133-
134-
String getMethodParameterName(int index) {
135-
136-
String parameterName = methodParameters[index].getParameterName();
137-
138-
if (parameterName == null) {
139-
parameterName = String.format("of type %s at index %d",
140-
ClassUtils.getShortName(methodParameters[index].getParameterType()), index);
141-
}
142-
143-
return parameterName;
144-
}
145-
146-
boolean isNullableReturn() {
147-
return nullableReturn;
148-
}
149-
150-
boolean isNullableParameter(int index) {
151-
return nullableParameters[index];
152-
}
153-
154-
private static boolean isNullableParameter(MethodParameter parameter) {
155-
156-
return requiresNoValue(parameter) || NullableUtils.isExplicitNullable(parameter)
157-
|| (KotlinReflectionUtils.isSupportedKotlinClass(parameter.getDeclaringClass())
158-
&& ReflectionUtils.isNullable(parameter));
159-
}
160-
161-
private static boolean requiresNoValue(MethodParameter parameter) {
162-
return ReflectionUtils.isVoid(parameter.getParameterType());
163-
}
164-
165-
public boolean[] getNullableParameters() {
166-
return this.nullableParameters;
167-
}
168-
169-
public MethodParameter[] getMethodParameters() {
170-
return this.methodParameters;
171-
}
172-
173-
@Override
174-
public boolean equals(@Nullable Object o) {
175-
176-
if (this == o) {
177-
return true;
178-
}
179-
180-
if (!(o instanceof Nullability that)) {
181-
return false;
182-
}
183-
184-
if (nullableReturn != that.nullableReturn) {
185-
return false;
186-
}
187-
188-
if (!ObjectUtils.nullSafeEquals(nullableParameters, that.nullableParameters)) {
189-
return false;
190-
}
191-
192-
return ObjectUtils.nullSafeEquals(methodParameters, that.methodParameters);
193-
}
194-
195-
@Override
196-
public int hashCode() {
197-
int result = (nullableReturn ? 1 : 0);
198-
result = (31 * result) + ObjectUtils.nullSafeHashCode(nullableParameters);
199-
result = (31 * result) + ObjectUtils.nullSafeHashCode(methodParameters);
200-
return result;
201-
}
34+
@Deprecated // TODO: do we want to remove this with next major
35+
public class MethodInvocationValidator extends NullabilityMethodInvocationValidator {
20236

203-
@Override
204-
public String toString() {
205-
return "MethodInvocationValidator.Nullability(nullableReturn=" + this.isNullableReturn() + ", nullableParameters="
206-
+ java.util.Arrays.toString(this.getNullableParameters()) + ", methodParameters="
207-
+ java.util.Arrays.deepToString(this.getMethodParameters()) + ")";
208-
}
209-
}
37+
public MethodInvocationValidator() {
38+
super((invocation) -> new EmptyResultDataAccessException("Result must not be null", 1));
39+
}
21040
}

‎src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import org.springframework.data.repository.util.QueryExecutionConverters;
7474
import org.springframework.data.spel.EvaluationContextProvider;
7575
import org.springframework.data.util.Lazy;
76+
import org.springframework.data.util.NullabilityMethodInvocationValidator;
7677
import org.springframework.data.util.ReflectionUtils;
7778
import org.springframework.expression.ExpressionParser;
7879
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -334,7 +335,7 @@ public <T> T getRepository(Class<T> repositoryInterface, Object customImplementa
334335
* @return the implemented repository interface.
335336
* @since 2.0
336337
*/
337-
@SuppressWarnings({ "unchecked" })
338+
@SuppressWarnings({ "unchecked", "deprecation" })
338339
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
339340

340341
if (logger.isDebugEnabled()) {
@@ -399,7 +400,7 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
399400
result.setTarget(target);
400401
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
401402

402-
if (MethodInvocationValidator.supports(repositoryInterface)) {
403+
if (NullabilityMethodInvocationValidator.supports(repositoryInterface)) {
403404
if (logger.isTraceEnabled()) {
404405
logger.trace(LogMessage.format("Register MethodInvocationValidator for %s...", repositoryInterface.getName()));
405406
}

0 commit comments

Comments
(0)

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