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.mapping.PersistentProperty;
|
||||||
import org.springframework.data.util.Lazy;
|
import org.springframework.data.util.Lazy;
|
||||||
import org.springframework.data.util.Optionals;
|
import org.springframework.data.util.Optionals;
|
||||||
|
import org.springframework.data.util.StreamUtils;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -229,22 +230,13 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
|
private <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
|
||||||
|
|
||||||
if (annotationCache != null && annotationCache.containsKey(annotationType)) {
|
return (Optional<A>) annotationCache.computeIfAbsent(annotationType, type -> {
|
||||||
return (Optional<A>) annotationCache.get(annotationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cacheAndReturn(annotationType, getAccessors()//
|
return getAccessors() //
|
||||||
.flatMap(it -> {
|
.map(it -> AnnotatedElementUtils.findMergedAnnotation(it, type)) //
|
||||||
|
.flatMap(StreamUtils::fromNullable) //
|
||||||
A mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(it, annotationType);
|
.findFirst();
|
||||||
|
});
|
||||||
if (mergedAnnotation == null) {
|
|
||||||
return Stream.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stream.of(mergedAnnotation);
|
|
||||||
})//
|
|
||||||
.findFirst());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -260,18 +252,6 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
|
|||||||
return annotation != null ? annotation : getOwner().findAnnotation(annotationType);
|
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.
|
* 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.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
@@ -95,4 +96,15 @@ public interface StreamUtils {
|
|||||||
Function<T, V> valueFunction) {
|
Function<T, V> valueFunction) {
|
||||||
return MultiValueMapCollector.of(keyFunction, 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