Skip to content

Commit 622c6f3

Browse files
committed
Deprecate NullableUtils.
1 parent ea6fbbb commit 622c6f3

File tree

4 files changed

+110
-69
lines changed

4 files changed

+110
-69
lines changed

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

+27-19
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,18 @@
2222

2323
import org.aopalliance.intercept.MethodInterceptor;
2424
import org.aopalliance.intercept.MethodInvocation;
25+
2526
import org.springframework.core.DefaultParameterNameDiscoverer;
2627
import org.springframework.core.KotlinDetector;
2728
import org.springframework.core.MethodParameter;
2829
import org.springframework.core.ParameterNameDiscoverer;
2930
import org.springframework.dao.EmptyResultDataAccessException;
3031
import org.springframework.data.util.KotlinReflectionUtils;
31-
import org.springframework.data.util.NullableUtils;
32+
import org.springframework.data.util.Nullability;
33+
import org.springframework.data.util.Nullability.MethodNullability;
3234
import org.springframework.data.util.ReflectionUtils;
3335
import org.springframework.lang.Nullable;
3436
import org.springframework.util.ClassUtils;
35-
import org.springframework.util.ConcurrentReferenceHashMap;
36-
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
3737
import org.springframework.util.ObjectUtils;
3838

3939
/**
@@ -45,12 +45,12 @@
4545
* @since 2.0
4646
* @see org.springframework.lang.NonNull
4747
* @see ReflectionUtils#isNullable(MethodParameter)
48-
* @see NullableUtils
48+
* @see org.springframework.data.util.Nullability
4949
*/
5050
public class MethodInvocationValidator implements MethodInterceptor {
5151

5252
private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
53-
private final Map<Method, Nullability> nullabilityCache = new ConcurrentHashMap<>(16);
53+
private final Map<Method, CachedNullability> nullabilityCache = new ConcurrentHashMap<>(16);
5454

5555
/**
5656
* Returns {@literal true} if the {@code repositoryInterface} is supported by this interceptor.
@@ -60,21 +60,26 @@ public class MethodInvocationValidator implements MethodInterceptor {
6060
*/
6161
public static boolean supports(Class<?> repositoryInterface) {
6262

63-
return KotlinDetector.isKotlinPresent() && KotlinReflectionUtils.isSupportedKotlinClass(repositoryInterface)
64-
|| NullableUtils.isNonNull(repositoryInterface, ElementType.METHOD)
65-
|| NullableUtils.isNonNull(repositoryInterface, ElementType.PARAMETER);
63+
if (KotlinDetector.isKotlinPresent() && KotlinReflectionUtils.isSupportedKotlinClass(repositoryInterface)) {
64+
return true;
65+
}
66+
67+
org.springframework.data.util.Nullability.Introspector introspector = org.springframework.data.util.Nullability
68+
.introspect(repositoryInterface);
69+
70+
return introspector.isDeclared(ElementType.METHOD) || introspector.isDeclared(ElementType.PARAMETER);
6671
}
6772

6873
@Nullable
6974
@Override
7075
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
7176

7277
Method method = invocation.getMethod();
73-
Nullability nullability = nullabilityCache.get(method);
78+
CachedNullability nullability = nullabilityCache.get(method);
7479

7580
if (nullability == null) {
7681

77-
nullability = Nullability.of(method, discoverer);
82+
nullability = CachedNullability.of(method, discoverer);
7883
nullabilityCache.put(method, nullability);
7984
}
8085

@@ -102,33 +107,36 @@ public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) thro
102107
return result;
103108
}
104109

105-
static final class Nullability {
110+
static final class CachedNullability {
106111

107112
private final boolean nullableReturn;
108113
private final boolean[] nullableParameters;
109114
private final MethodParameter[] methodParameters;
110115

111-
private Nullability(boolean nullableReturn, boolean[] nullableParameters, MethodParameter[] methodParameters) {
116+
private CachedNullability(boolean nullableReturn, boolean[] nullableParameters,
117+
MethodParameter[] methodParameters) {
112118
this.nullableReturn = nullableReturn;
113119
this.nullableParameters = nullableParameters;
114120
this.methodParameters = methodParameters;
115121
}
116122

117-
static Nullability of(Method method, ParameterNameDiscoverer discoverer) {
123+
static CachedNullability of(Method method, ParameterNameDiscoverer discoverer) {
124+
125+
MethodNullability methodNullability = Nullability.forMethod(method);
118126

119-
boolean nullableReturn = isNullableParameter(new MethodParameter(method, -1));
127+
boolean nullableReturn = isNullableParameter(methodNullability, new MethodParameter(method, -1));
120128
boolean[] nullableParameters = new boolean[method.getParameterCount()];
121129
MethodParameter[] methodParameters = new MethodParameter[method.getParameterCount()];
122130

123131
for (int i = 0; i < method.getParameterCount(); i++) {
124132

125133
MethodParameter parameter = new MethodParameter(method, i);
126134
parameter.initParameterNameDiscovery(discoverer);
127-
nullableParameters[i] = isNullableParameter(parameter);
135+
nullableParameters[i] = isNullableParameter(methodNullability, parameter);
128136
methodParameters[i] = parameter;
129137
}
130138

131-
return new Nullability(nullableReturn, nullableParameters, methodParameters);
139+
return new CachedNullability(nullableReturn, nullableParameters, methodParameters);
132140
}
133141

134142
String getMethodParameterName(int index) {
@@ -151,9 +159,9 @@ boolean isNullableParameter(int index) {
151159
return nullableParameters[index];
152160
}
153161

154-
private static boolean isNullableParameter(MethodParameter parameter) {
162+
private static boolean isNullableParameter(MethodNullability methodNullability, MethodParameter parameter) {
155163

156-
return requiresNoValue(parameter) || NullableUtils.isExplicitNullable(parameter)
164+
return requiresNoValue(parameter) || methodNullability.forParameter(parameter).isNullable()
157165
|| (KotlinReflectionUtils.isSupportedKotlinClass(parameter.getDeclaringClass())
158166
&& ReflectionUtils.isNullable(parameter));
159167
}
@@ -177,7 +185,7 @@ public boolean equals(@Nullable Object o) {
177185
return true;
178186
}
179187

180-
if (!(o instanceof Nullability that)) {
188+
if (!(o instanceof CachedNullability that)) {
181189
return false;
182190
}
183191

src/main/java/org/springframework/data/util/Nullability.java

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.util;
1717

18+
import java.lang.annotation.ElementType;
1819
import java.lang.reflect.Method;
1920
import java.lang.reflect.Parameter;
2021

@@ -188,6 +189,14 @@ static Introspector introspect(Package pkg) {
188189
*/
189190
interface Introspector {
190191

192+
/**
193+
* Returns whether nullability rules are defined for the given {@link ElementType}.
194+
*
195+
* @param elementType the element type to check.
196+
* @return {@code true} if nullability is declared for the given element type; {@code false} otherwise.
197+
*/
198+
boolean isDeclared(ElementType elementType);
199+
191200
/**
192201
* Creates a new {@link MethodNullability} instance by introspecting the {@link Method}.
193202
* <p>

src/main/java/org/springframework/data/util/NullabilityIntrospector.java

+65-10
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import java.lang.annotation.Annotation;
1919
import java.lang.annotation.ElementType;
2020
import java.lang.reflect.AnnotatedElement;
21-
import java.lang.reflect.AnnotatedType;
2221
import java.lang.reflect.Executable;
2322
import java.lang.reflect.Method;
2423
import java.lang.reflect.Parameter;
@@ -33,13 +32,16 @@
3332
import java.util.function.Function;
3433
import java.util.function.Predicate;
3534

35+
import org.springframework.core.annotation.AnnotatedElementUtils;
36+
import org.springframework.core.annotation.AnnotationUtils;
3637
import org.springframework.core.annotation.MergedAnnotations;
3738
import org.springframework.lang.NonNull;
3839
import org.springframework.lang.NonNullApi;
3940
import org.springframework.lang.NonNullFields;
4041
import org.springframework.lang.Nullable;
4142
import org.springframework.util.ClassUtils;
4243
import org.springframework.util.ConcurrentLruCache;
44+
import org.springframework.util.MultiValueMap;
4345

4446
/**
4547
* Default {@link Nullability.Introspector} implementation backed by {@link NullabilityProvider nullability providers}.
@@ -111,6 +113,11 @@ static DeclarationAnchor createTree(AnnotatedElement element) {
111113
throw new IllegalArgumentException(String.format("Cannot create DeclarationAnchor for %s", element));
112114
}
113115

116+
@Override
117+
public boolean isDeclared(ElementType elementType) {
118+
return anchor.evaluate(elementType) != Spec.UNSPECIFIED;
119+
}
120+
114121
@Override
115122
public Nullability.MethodNullability forMethod(Method method) {
116123

@@ -138,7 +145,7 @@ public Nullability forParameter(Parameter parameter) {
138145
return new TheNullability(element.evaluate(ElementType.PARAMETER));
139146
}
140147

141-
static <T> Spec doWith(Function<NullabilityProvider, Spec> function) {
148+
static Spec doWith(Function<NullabilityProvider, Spec> function) {
142149

143150
for (NullabilityProvider provider : providers) {
144151
Spec result = function.apply(provider);
@@ -151,6 +158,34 @@ static <T> Spec doWith(Function<NullabilityProvider, Spec> function) {
151158
return Spec.UNSPECIFIED;
152159
}
153160

161+
@SuppressWarnings("unchecked")
162+
static <T> boolean test(Annotation annotation, String metaAnnotationName, String attribute, Predicate<T> filter) {
163+
164+
if (annotation.annotationType().getName().equals(metaAnnotationName)) {
165+
166+
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
167+
168+
return !attributes.isEmpty() && filter.test((T) attributes.get(attribute));
169+
}
170+
171+
MultiValueMap<String, Object> attributes = AnnotatedElementUtils
172+
.getAllAnnotationAttributes(annotation.annotationType(), metaAnnotationName);
173+
174+
if (attributes == null || attributes.isEmpty()) {
175+
return false;
176+
}
177+
178+
List<Object> elementTypes = attributes.get(attribute);
179+
180+
for (Object value : elementTypes) {
181+
182+
if (filter.test((T) value)) {
183+
return true;
184+
}
185+
}
186+
return false;
187+
}
188+
154189
/**
155190
* Provider for nullability rules.
156191
*/
@@ -208,6 +243,7 @@ Spec evaluate(AnnotatedElement element, ElementType elementType) {
208243
* {@code @Nonnull}/{@code @Nullable} directly or through meta-annotations that are composed of
209244
* {@code @Nonnull}/{@code @Nullable} and {@code @TypeQualifierDefault}.
210245
*/
246+
@SuppressWarnings("DataFlowIssue")
211247
static class Jsr305Provider extends NullabilityProvider {
212248

213249
private static final Class<Annotation> NON_NULL = findClass("javax.annotation.Nonnull");
@@ -269,7 +305,7 @@ private static boolean test(Class<Annotation> annotationClass, Annotation annota
269305
}
270306

271307
private static boolean isInScope(Annotation annotation, ElementType elementType) {
272-
return NullableUtils.test(annotation, TYPE_QUALIFIER_CLASS_NAME, "value",
308+
return NullabilityIntrospector.test(annotation, TYPE_QUALIFIER_CLASS_NAME, "value",
273309
(ElementType[] o) -> Arrays.binarySearch(o, elementType) >= 0);
274310
}
275311

@@ -281,24 +317,27 @@ private static boolean isInScope(Annotation annotation, ElementType elementType)
281317
* @return {@literal true} if the annotation expresses non-nullability.
282318
*/
283319
static boolean isNonNull(Annotation annotation) {
284-
return NullableUtils.test(annotation, NON_NULL.getName(), "when", o -> WHEN_NON_NULLABLE.contains(o.toString()));
320+
return NullabilityIntrospector.test(annotation, NON_NULL.getName(), "when",
321+
o -> WHEN_NON_NULLABLE.contains(o.toString()));
285322
}
286323

287324
/**
288325
* Introspect {@link Annotation} for being either a meta-annotation composed of {@code Nonnull} or {@code Nonnull}
289326
* itself expressing nullability.
290327
*
291-
* @param annotation
328+
* @param annotation the annotation to introspect.
292329
* @return {@literal true} if the annotation expresses nullability.
293330
*/
294331
static boolean isNullable(Annotation annotation) {
295-
return NullableUtils.test(annotation, NON_NULL.getName(), "when", o -> WHEN_NULLABLE.contains(o.toString()));
332+
return NullabilityIntrospector.test(annotation, NON_NULL.getName(), "when",
333+
o -> WHEN_NULLABLE.contains(o.toString()));
296334
}
297335
}
298336

299337
/**
300338
* Simplified variant of {@link Jsr305Provider} without {@code when} and {@code @TypeQualifierDefault} support.
301339
*/
340+
@SuppressWarnings("DataFlowIssue")
302341
static class JakartaAnnotationProvider extends SimpleAnnotationNullabilityProvider {
303342

304343
private static final Class<Annotation> NON_NULL = findClass("jakarta.annotation.Nonnull");
@@ -316,6 +355,7 @@ public static boolean isAvailable() {
316355
/**
317356
* Provider for JSpecify annotations.
318357
*/
358+
@SuppressWarnings("DataFlowIssue")
319359
static class JSpecifyAnnotationProvider extends NullabilityProvider {
320360

321361
private static final Class<Annotation> NON_NULL = findClass("org.jspecify.annotations.NonNull");
@@ -331,7 +371,6 @@ public static boolean isAvailable() {
331371
Spec evaluate(AnnotatedElement element, ElementType elementType) {
332372

333373
Annotation[] annotations = element.getAnnotations();
334-
AnnotatedType annotatedType = null;
335374

336375
if (element instanceof Parameter p) {
337376

@@ -478,8 +517,24 @@ public Nullability forParameter(Parameter parameter) {
478517
}
479518
}
480519

520+
/**
521+
* Declaration result.
522+
*/
481523
enum Spec {
482-
UNSPECIFIED, NULLABLE, NON_NULL
524+
/**
525+
* No nullabilty rule declared.
526+
*/
527+
UNSPECIFIED,
528+
529+
/**
530+
* Declaration yields nullable.
531+
*/
532+
NULLABLE,
533+
534+
/**
535+
* Declaration yields non-nullable.
536+
*/
537+
NON_NULL
483538
}
484539

485540
/**
@@ -490,8 +545,8 @@ interface DeclarationAnchor {
490545
/**
491546
* Evaluate nullability declarations for the given {@link ElementType}.
492547
*
493-
* @param target
494-
* @return
548+
* @param target target element type to evaluate declarations for.
549+
* @return specification result.
495550
*/
496551
Spec evaluate(ElementType target);
497552

0 commit comments

Comments
 (0)