DATACMNS-1271 - Streamlined implementation of AnnotationBasedPersistentProperty to avoid NullPointerExceptions.
We now use the annotation cache's ….computeIfAbsent(…) to avoid inconsistencies between ….contains(…) and ….get(…) in multi-threaded scenarios.
This commit is contained in:
@@ -39,6 +39,7 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.Optionals;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -229,22 +230,13 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
|
||||
@SuppressWarnings("unchecked")
|
||||
private <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
|
||||
|
||||
if (annotationCache != null && annotationCache.containsKey(annotationType)) {
|
||||
return (Optional<A>) annotationCache.get(annotationType);
|
||||
}
|
||||
return (Optional<A>) annotationCache.computeIfAbsent(annotationType, type -> {
|
||||
|
||||
return cacheAndReturn(annotationType, getAccessors()//
|
||||
.flatMap(it -> {
|
||||
|
||||
A mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(it, annotationType);
|
||||
|
||||
if (mergedAnnotation == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
return Stream.of(mergedAnnotation);
|
||||
})//
|
||||
.findFirst());
|
||||
return getAccessors() //
|
||||
.map(it -> AnnotatedElementUtils.findMergedAnnotation(it, type)) //
|
||||
.flatMap(StreamUtils::fromNullable) //
|
||||
.findFirst();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -260,18 +252,6 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
|
||||
return annotation != null ? annotation : getOwner().findAnnotation(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given annotation into the local cache and returns it.
|
||||
*
|
||||
* @param annotation
|
||||
* @return
|
||||
*/
|
||||
private <A extends Annotation> Optional<A> cacheAndReturn(Class<? extends A> type, Optional<A> annotation) {
|
||||
|
||||
annotationCache.put(type, annotation);
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the property carries the an annotation of the given type.
|
||||
*
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
@@ -95,4 +96,15 @@ public interface StreamUtils {
|
||||
Function<T, V> valueFunction) {
|
||||
return MultiValueMapCollector.of(keyFunction, valueFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Stream} for the given value returning an empty {@link Stream} if the value is {@literal null}.
|
||||
*
|
||||
* @param source can be {@literal null}.
|
||||
* @return a new {@link Stream} for the given value returning an empty {@link Stream} if the value is {@literal null}.
|
||||
* @since 2.0.6
|
||||
*/
|
||||
public static <T> Stream<T> fromNullable(@Nullable T source) {
|
||||
return source == null ? Stream.empty() : Stream.of(source);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user