Browse Source

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.
pull/271/merge
Oliver Gierke 8 years ago
parent
commit
78d21327bb
  1. 34
      src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
  2. 12
      src/main/java/org/springframework/data/util/StreamUtils.java

34
src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java

@ -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()//
.flatMap(it -> {
A mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(it, annotationType);
if (mergedAnnotation == null) { return getAccessors() //
return Stream.empty(); .map(it -> AnnotatedElementUtils.findMergedAnnotation(it, type)) //
} .flatMap(StreamUtils::fromNullable) //
.findFirst();
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.
* *

12
src/main/java/org/springframework/data/util/StreamUtils.java

@ -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);
}
} }

Loading…
Cancel
Save