Browse Source

DATACMNS-1271 - Streamlined implementation of AnnotationBasedPersistentProperty to avoid NullPointerExceptions.

Java 6 compatible backport of 78d21327 and 7e7d4baa.
pull/301/head
Oliver Gierke 8 years ago
parent
commit
aadeebbb7d
No known key found for this signature in database
GPG Key ID: 6E42B5787543F690
  1. 52
      src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
  2. 3
      src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java

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

@ -20,8 +20,8 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -44,13 +44,14 @@ import org.springframework.util.Assert;
* @author Oliver Gierke * @author Oliver Gierke
* @author Christoph Strobl * @author Christoph Strobl
*/ */
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>> extends public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>>
AbstractPersistentProperty<P> { extends AbstractPersistentProperty<P> {
private static final String SPRING_DATA_PACKAGE = "org.springframework.data"; private static final String SPRING_DATA_PACKAGE = "org.springframework.data";
private final Value value; private final Value value;
private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>(); private final Map<Class<? extends Annotation>, CachedValue<? extends Annotation>> annotationCache = //
new ConcurrentHashMap<Class<? extends Annotation>, CachedValue<? extends Annotation>>();
private Boolean isTransient; private Boolean isTransient;
private boolean usePropertyAccess; private boolean usePropertyAccess;
@ -93,11 +94,12 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
Class<? extends Annotation> annotationType = annotation.annotationType(); Class<? extends Annotation> annotationType = annotation.annotationType();
validateAnnotation(annotation, "Ambiguous mapping! Annotation %s configured " validateAnnotation(annotation,
+ "multiple times on accessor methods of property %s in class %s!", annotationType.getSimpleName(), "Ambiguous mapping! Annotation %s configured "
getName(), getOwner().getType().getSimpleName()); + "multiple times on accessor methods of property %s in class %s!",
annotationType.getSimpleName(), getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType, annotation); cacheAndReturn(annotationType, annotation);
} }
} }
@ -109,11 +111,11 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
Class<? extends Annotation> annotationType = annotation.annotationType(); Class<? extends Annotation> annotationType = annotation.annotationType();
validateAnnotation(annotation, "Ambiguous mapping! Annotation %s configured " validateAnnotation(annotation,
+ "on field %s and one of its accessor methods in class %s!", annotationType.getSimpleName(), "Ambiguous mapping! Annotation %s configured " + "on field %s and one of its accessor methods in class %s!",
field.getName(), getOwner().getType().getSimpleName()); annotationType.getSimpleName(), field.getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType, annotation); cacheAndReturn(annotationType, annotation);
} }
} }
@ -133,7 +135,9 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
return; return;
} }
if (annotationCache.containsKey(annotationType) && !annotationCache.get(annotationType).equals(candidate)) { CachedValue<? extends Annotation> cachedValue = annotationCache.get(annotationType);
if (cachedValue != null && !annotationCache.get(annotationType).value.equals(candidate)) {
throw new MappingException(String.format(message, arguments)); throw new MappingException(String.format(message, arguments));
} }
} }
@ -151,7 +155,7 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
/** /**
* Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as * Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as
* transien. * transient.
* *
* @see org.springframework.data.mapping.BasicPersistentProperty#isTransient() * @see org.springframework.data.mapping.BasicPersistentProperty#isTransient()
*/ */
@ -213,8 +217,11 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
Assert.notNull(annotationType, "Annotation type must not be null!"); Assert.notNull(annotationType, "Annotation type must not be null!");
if (annotationCache != null && annotationCache.containsKey(annotationType)) { CachedValue<? extends Annotation> cachedAnnotation = annotationCache == null ? null
return (A) annotationCache.get(annotationType); : annotationCache.get(annotationType);
if (cachedAnnotation != null) {
return (A) cachedAnnotation.getValue();
} }
for (Method method : Arrays.asList(getGetter(), getSetter())) { for (Method method : Arrays.asList(getGetter(), getSetter())) {
@ -254,7 +261,7 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
private <A extends Annotation> A cacheAndReturn(Class<? extends A> type, A annotation) { private <A extends Annotation> A cacheAndReturn(Class<? extends A> type, A annotation) {
if (annotationCache != null) { if (annotationCache != null) {
annotationCache.put(type, annotation); annotationCache.put(type, CachedValue.of(annotation));
} }
return annotation; return annotation;
@ -292,12 +299,17 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (Annotation annotation : annotationCache.values()) { for (CachedValue<? extends Annotation> annotation : annotationCache.values()) {
if (annotation != null) { if (annotation.value != null) {
builder.append(annotation.toString()).append(" "); builder.append(annotation.value.toString()).append(" ");
} }
} }
return builder.toString() + super.toString(); return builder.toString() + super.toString();
} }
@lombok.Value(staticConstructor = "of")
static class CachedValue<T> {
T value;
}
} }

3
src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java

@ -38,6 +38,7 @@ import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
import org.springframework.data.mapping.context.SampleMappingContext; import org.springframework.data.mapping.context.SampleMappingContext;
import org.springframework.data.mapping.context.SamplePersistentProperty; import org.springframework.data.mapping.context.SamplePersistentProperty;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty.CachedValue;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
/** /**
@ -168,7 +169,7 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
Map<Class<?>, ?> field = (Map<Class<?>, ?>) ReflectionTestUtils.getField(property, "annotationCache"); Map<Class<?>, ?> field = (Map<Class<?>, ?>) ReflectionTestUtils.getField(property, "annotationCache");
assertThat(field.containsKey(MyAnnotation.class), is(true)); assertThat(field.containsKey(MyAnnotation.class), is(true));
assertThat(field.get(MyAnnotation.class), is(nullValue())); assertThat(field.get(MyAnnotation.class), is((Object) CachedValue.of(null)));
} }
@Test // DATACMNS-825 @Test // DATACMNS-825

Loading…
Cancel
Save