replaced multiple caches in ReflectingFieldNavigator with a single one.

Two additional optimizations:
- storing the field list for the entire type hierarchy in the cache instead of navigating it every time.
- storing the resolved type for the field in FieldAttributes instead of using reflection every time.
This commit is contained in:
Inderjeet Singh 2011-04-01 23:54:41 +00:00
parent 9c894c7485
commit bf4ab04413
2 changed files with 73 additions and 67 deletions

View File

@ -17,6 +17,7 @@
package com.google.gson; package com.google.gson;
import com.google.gson.internal.$Preconditions; import com.google.gson.internal.$Preconditions;
import com.google.gson.internal.$Types;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -48,23 +49,30 @@ public final class FieldAttributes {
private final boolean isSynthetic; private final boolean isSynthetic;
private final int modifiers; private final int modifiers;
private final String name; private final String name;
private final Type resolvedType;
// Fields used for lazy initialization // Fields used for lazy initialization
private Type genericType; private Type genericType;
private Collection<Annotation> annotations; private Collection<Annotation> annotations;
FieldAttributes(Class<?> declaringClazz, Field f) {
this(declaringClazz, f, declaringClazz);
}
/** /**
* Constructs a Field Attributes object from the {@code f}. * Constructs a Field Attributes object from the {@code f}.
* *
* @param f the field to pull attributes from * @param f the field to pull attributes from
* @param declaringType The type in which the field is declared
*/ */
FieldAttributes(final Class<?> declaringClazz, final Field f) { FieldAttributes(Class<?> declaringClazz, Field f, Type declaringType) {
this.declaringClazz = $Preconditions.checkNotNull(declaringClazz); this.declaringClazz = $Preconditions.checkNotNull(declaringClazz);
this.name = f.getName(); this.name = f.getName();
this.declaredType = f.getType(); this.declaredType = f.getType();
this.isSynthetic = f.isSynthetic(); this.isSynthetic = f.isSynthetic();
this.modifiers = f.getModifiers(); this.modifiers = f.getModifiers();
this.field = f; this.field = f;
this.resolvedType = getTypeInfoForField(f, declaringType);
} }
private static int getMaxCacheSize() { private static int getMaxCacheSize() {
@ -217,6 +225,10 @@ public final class FieldAttributes {
return field; return field;
} }
Type getResolvedType() {
return resolvedType;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T extends Annotation> T getAnnotationFromArray( private static <T extends Annotation> T getAnnotationFromArray(
Collection<Annotation> annotations, Class<T> annotation) { Collection<Annotation> annotations, Class<T> annotation) {
@ -227,4 +239,21 @@ public final class FieldAttributes {
} }
return null; return null;
} }
/**
* Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a
* "TypeVariable" in a parameterized type then it evaluates the real type.
*
* @param f the actual field object to retrieve the type from
* @param typeDefiningF the type that contains the field {@code f}
* @return the type information for the field
*/
public static Type getTypeInfoForField(Field f, Type typeDefiningF) {
Class<?> rawType = $Types.getRawType(typeDefiningF);
if (!f.getDeclaringClass().isAssignableFrom(rawType)) {
// this field is unrelated to the type; the user probably omitted type information
return f.getGenericType();
}
return $Types.resolve(typeDefiningF, rawType, f.getGenericType());
}
} }

View File

@ -33,10 +33,9 @@ import java.util.List;
* @author Jesse Wilson * @author Jesse Wilson
*/ */
final class ReflectingFieldNavigator { final class ReflectingFieldNavigator {
private static final LruCache<Type, List<Class<?>>> classCache =
new LruCache<Type, List<Class<?>>>(500); private static final LruCache<Type, List<FieldAttributes>> fieldsCache =
private static final LruCache<Class<?>, Field[]> fieldsCache = new LruCache<Type, List<FieldAttributes>>(500);
new LruCache<Class<?>, Field[]>(500);
private final ExclusionStrategy exclusionStrategy; private final ExclusionStrategy exclusionStrategy;
@ -53,18 +52,48 @@ final class ReflectingFieldNavigator {
* @param visitor the visitor to visit each field with * @param visitor the visitor to visit each field with
*/ */
void visitFieldsReflectively(ObjectTypePair objTypePair, Visitor visitor) { void visitFieldsReflectively(ObjectTypePair objTypePair, Visitor visitor) {
for (Class<?> curr : getInheritanceHierarchy(objTypePair.getMoreSpecificType())) { Type moreSpecificType = objTypePair.getMoreSpecificType();
navigateClassFields(objTypePair.getObject(), objTypePair.type, curr, visitor); Object obj = objTypePair.getObject();
for (FieldAttributes fieldAttributes : getAllFields(moreSpecificType, objTypePair.getType())) {
if (exclusionStrategy.shouldSkipField(fieldAttributes)
|| exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) {
continue; // skip
} }
Type resolvedTypeOfField = fieldAttributes.getResolvedType();
boolean visitedWithCustomHandler =
visitor.visitFieldUsingCustomHandler(fieldAttributes, resolvedTypeOfField, obj);
if (!visitedWithCustomHandler) {
if ($Types.isArray(resolvedTypeOfField)) {
visitor.visitArrayField(fieldAttributes, resolvedTypeOfField, obj);
} else {
visitor.visitObjectField(fieldAttributes, resolvedTypeOfField, obj);
}
}
}
}
private List<FieldAttributes> getAllFields(Type type, Type declaredType) {
List<FieldAttributes> fields = fieldsCache.get(type);
if (fields == null) {
fields = new ArrayList<FieldAttributes>();
for (Class<?> curr : getInheritanceHierarchy(type)) {
Field[] fields1 = curr.getDeclaredFields();
AccessibleObject.setAccessible(fields1, true);
Field[] classFields = fields1;
for (Field f : classFields) {
fields.add(new FieldAttributes(curr, f, declaredType));
}
}
fieldsCache.addElement(type, fields);
}
return fields;
} }
/** /**
* Returns a list of classes corresponding to the inheritance of specified type * Returns a list of classes corresponding to the inheritance of specified type
*/ */
private List<Class<?>> getInheritanceHierarchy(Type type) { private List<Class<?>> getInheritanceHierarchy(Type type) {
List<Class<?>> classes = classCache.get(type); List<Class<?>> classes = new ArrayList<Class<?>>();
if (classes == null) {
classes = new ArrayList<Class<?>>();
Class<?> topLevelClass = $Types.getRawType(type); Class<?> topLevelClass = $Types.getRawType(type);
for (Class<?> curr = topLevelClass; curr != null && !curr.equals(Object.class); curr = for (Class<?> curr = topLevelClass; curr != null && !curr.equals(Object.class); curr =
curr.getSuperclass()) { curr.getSuperclass()) {
@ -72,58 +101,6 @@ final class ReflectingFieldNavigator {
classes.add(curr); classes.add(curr);
} }
} }
classCache.put(type, classes);
}
return classes; return classes;
} }
private void navigateClassFields(Object obj, Type objType,
Class<?> classInInheritanceHierarchyForObj, Visitor visitor) {
Field[] fields = getFields(classInInheritanceHierarchyForObj);
for (Field f : fields) {
FieldAttributes fieldAttributes = new FieldAttributes(classInInheritanceHierarchyForObj, f);
if (exclusionStrategy.shouldSkipField(fieldAttributes)
|| exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) {
continue; // skip
}
Type declaredTypeOfField = getTypeInfoForField(f, objType);
boolean visitedWithCustomHandler =
visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj);
if (!visitedWithCustomHandler) {
if ($Types.isArray(declaredTypeOfField)) {
visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj);
} else {
visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj);
}
}
}
}
private Field[] getFields(Class<?> clazz) {
Field[] fields = fieldsCache.get(clazz);
if (fields == null) {
fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
fieldsCache.put(clazz, fields);
}
return fields;
}
/**
* Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a
* "TypeVariable" in a parameterized type then it evaluates the real type.
*
* @param f the actual field object to retrieve the type from
* @param typeDefiningF the type that contains the field {@code f}
* @return the type information for the field
*/
public static Type getTypeInfoForField(Field f, Type typeDefiningF) {
Class<?> rawType = $Types.getRawType(typeDefiningF);
if (!f.getDeclaringClass().isAssignableFrom(rawType)) {
// this field is unrelated to the type; the user probably omitted type information
return f.getGenericType();
}
return $Types.resolve(typeDefiningF, rawType, f.getGenericType());
}
} }