Skip to content

Commit 409b533

Browse files
committed
Merge pull request #146 from philwebb/SPR-9796
* SPR-9796: Update cache to support concurrent reads Cache and late resolve annotations for performance Polish Property class Develop ConcurrentReferenceHashMap
2 parents 5a0c4a9 + 3aa9ac1 commit 409b533

File tree

4 files changed

+1741
-36
lines changed

4 files changed

+1741
-36
lines changed

spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,22 @@
1616

1717
package org.springframework.core;
1818

19-
import java.lang.ref.Reference;
20-
import java.lang.ref.WeakReference;
2119
import java.lang.reflect.Array;
2220
import java.lang.reflect.GenericArrayType;
2321
import java.lang.reflect.Method;
2422
import java.lang.reflect.ParameterizedType;
2523
import java.lang.reflect.Type;
2624
import java.lang.reflect.TypeVariable;
2725
import java.lang.reflect.WildcardType;
28-
import java.util.Collections;
2926
import java.util.HashMap;
3027
import java.util.Map;
31-
import java.util.WeakHashMap;
3228

3329
import org.apache.commons.logging.Log;
3430
import org.apache.commons.logging.LogFactory;
3531

3632
import org.springframework.util.Assert;
3733
import org.springframework.util.ObjectUtils;
34+
import org.springframework.util.ConcurrentReferenceHashMap;
3835

3936
/**
4037
* Helper class for resolving generic types against type variables.
@@ -53,9 +50,8 @@ public abstract class GenericTypeResolver {
5350
private static final Log logger = LogFactory.getLog(GenericTypeResolver.class);
5451

5552
/** Cache from Class to TypeVariable Map */
56-
private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache =
57-
Collections.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
58-
53+
private static final Map<Class, Map<TypeVariable, Type>> typeVariableCache =
54+
new ConcurrentReferenceHashMap<Class, Map<TypeVariable,Type>>();
5955

6056
/**
6157
* Determine the target type for the given parameter specification.
@@ -408,8 +404,8 @@ static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap
408404
* all super types, enclosing types and interfaces.
409405
*/
410406
public static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
411-
Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
412-
Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref.get() : null);
407+
Map<TypeVariable, Type> ref = typeVariableCache.get(clazz);
408+
Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref : null);
413409

414410
if (typeVariableMap == null) {
415411
typeVariableMap = new HashMap<TypeVariable, Type>();
@@ -441,7 +437,7 @@ public static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
441437
type = type.getEnclosingClass();
442438
}
443439

444-
typeVariableCache.put(clazz, new WeakReference<Map<TypeVariable, Type>>(typeVariableMap));
440+
typeVariableCache.put(clazz, typeVariableMap);
445441
}
446442

447443
return typeVariableMap;

spring-core/src/main/java/org/springframework/core/convert/Property.java

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
package org.springframework.core.convert;
1818

1919
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
2021
import java.lang.reflect.Field;
2122
import java.lang.reflect.Method;
2223
import java.util.LinkedHashMap;
2324
import java.util.Map;
2425

2526
import org.springframework.core.GenericTypeResolver;
2627
import org.springframework.core.MethodParameter;
28+
import org.springframework.util.ConcurrentReferenceHashMap;
29+
import org.springframework.util.ObjectUtils;
2730
import org.springframework.util.ReflectionUtils;
2831
import org.springframework.util.StringUtils;
2932

@@ -37,12 +40,16 @@
3740
* The built TypeDescriptor can then be used to convert from/to the property type.
3841
*
3942
* @author Keith Donald
43+
* @author Phillip Webb
4044
* @since 3.1
4145
* @see TypeDescriptor#TypeDescriptor(Property)
4246
* @see TypeDescriptor#nested(Property, int)
4347
*/
4448
public final class Property {
4549

50+
private static Map<Property, Annotation[]> annotationCache =
51+
new ConcurrentReferenceHashMap<Property, Annotation[]>();
52+
4653
private final Class<?> objectType;
4754

4855
private final Method readMethod;
@@ -53,8 +60,7 @@ public final class Property {
5360

5461
private final MethodParameter methodParameter;
5562

56-
private final Annotation[] annotations;
57-
63+
private Annotation[] annotations;
5864

5965
public Property(Class<?> objectType, Method readMethod, Method writeMethod) {
6066
this(objectType, readMethod, writeMethod, null);
@@ -65,13 +71,7 @@ public Property(Class<?> objectType, Method readMethod, Method writeMethod, Stri
6571
this.readMethod = readMethod;
6672
this.writeMethod = writeMethod;
6773
this.methodParameter = resolveMethodParameter();
68-
if (name != null) {
69-
this.name = name;
70-
}
71-
else {
72-
this.name = resolveName();
73-
}
74-
this.annotations = resolveAnnotations();
74+
this.name = (name == null ? resolveName() : name);
7575
}
7676

7777

@@ -118,6 +118,9 @@ MethodParameter getMethodParameter() {
118118
}
119119

120120
Annotation[] getAnnotations() {
121+
if(this.annotations == null) {
122+
this.annotations = resolveAnnotations();
123+
}
121124
return this.annotations;
122125
}
123126

@@ -188,26 +191,26 @@ private MethodParameter resolveParameterType(MethodParameter parameter) {
188191
}
189192

190193
private Annotation[] resolveAnnotations() {
191-
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
192-
Method readMethod = getReadMethod();
193-
if (readMethod != null) {
194-
for (Annotation ann : readMethod.getAnnotations()) {
195-
annMap.put(ann.annotationType(), ann);
196-
}
197-
}
198-
Method writeMethod = getWriteMethod();
199-
if (writeMethod != null) {
200-
for (Annotation ann : writeMethod.getAnnotations()) {
201-
annMap.put(ann.annotationType(), ann);
202-
}
194+
Annotation[] annotations = annotationCache.get(this);
195+
if(annotations == null) {
196+
Map<Class<? extends Annotation>, Annotation> annotationMap = new LinkedHashMap<Class<? extends Annotation>, Annotation>();
197+
addAnnotationsToMap(annotationMap, getReadMethod());
198+
addAnnotationsToMap(annotationMap, getWriteMethod());
199+
addAnnotationsToMap(annotationMap, getField());
200+
annotations = annotationMap.values().toArray(new Annotation[annotationMap.size()]);
201+
annotationCache.put(this, annotations);
203202
}
204-
Field field = getField();
205-
if (field != null) {
206-
for (Annotation ann : field.getAnnotations()) {
207-
annMap.put(ann.annotationType(), ann);
203+
return annotations;
204+
}
205+
206+
private void addAnnotationsToMap(
207+
Map<Class<? extends Annotation>, Annotation> annotationMap,
208+
AnnotatedElement object) {
209+
if (object != null) {
210+
for (Annotation annotation : object.getAnnotations()) {
211+
annotationMap.put(annotation.annotationType(), annotation);
208212
}
209213
}
210-
return annMap.values().toArray(new Annotation[annMap.size()]);
211214
}
212215

213216
private Field getField() {
@@ -238,4 +241,34 @@ private Class<?> declaringClass() {
238241
}
239242
}
240243

244+
@Override
245+
public int hashCode() {
246+
final int prime = 31;
247+
int hashCode = 1;
248+
hashCode = prime * hashCode + ObjectUtils.nullSafeHashCode(objectType);
249+
hashCode = prime * hashCode + ObjectUtils.nullSafeHashCode(readMethod);
250+
hashCode = prime * hashCode + ObjectUtils.nullSafeHashCode(writeMethod);
251+
hashCode = prime * hashCode + ObjectUtils.nullSafeHashCode(name);
252+
return hashCode;
253+
}
254+
255+
@Override
256+
public boolean equals(Object obj) {
257+
if (this == obj) {
258+
return true;
259+
}
260+
if (obj == null) {
261+
return false;
262+
}
263+
if (getClass() != obj.getClass()) {
264+
return false;
265+
}
266+
Property other = (Property) obj;
267+
boolean equals = true;
268+
equals &= ObjectUtils.nullSafeEquals(objectType, other.objectType);
269+
equals &= ObjectUtils.nullSafeEquals(readMethod, other.readMethod);
270+
equals &= ObjectUtils.nullSafeEquals(writeMethod, other.writeMethod);
271+
equals &= ObjectUtils.nullSafeEquals(name, other.name);
272+
return equals;
273+
}
241274
}

0 commit comments

Comments
 (0)