Skip to content

Commit 7ae4103

Browse files
committed
Cache Fields by annotation, class
For context injection we frequently need to find all fields in a class with a given annotation. There is no reason to expect this information will change at runtime so we should cache these sets to improve the performance of injection.
1 parent 3eeab7c commit 7ae4103

File tree

1 file changed

+60
-1
lines changed

1 file changed

+60
-1
lines changed

src/main/java/org/scijava/util/ClassUtils.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
import java.net.MalformedURLException;
4141
import java.net.URL;
4242
import java.util.ArrayList;
43+
import java.util.HashMap;
4344
import java.util.List;
45+
import java.util.Map;
4446

4547
/**
4648
* Useful methods for working with {@link Class} objects and primitive types.
@@ -53,6 +55,18 @@ private ClassUtils() {
5355
// prevent instantiation of utility class
5456
}
5557

58+
/**
59+
* This maps a base class (key1) to a map of annotation classes (key2), which
60+
* then maps to a list of {@link Field} instances, being the set of fields in
61+
* the base class with the specified annotation.
62+
* <p>
63+
* This map serves as a cache, as these annotations should not change at
64+
* runtime and thus can alleviate the frequency field querying.
65+
* </p>
66+
*/
67+
private static final Map<Class<?>, Map<Class<?>, List<Field>>> fields =
68+
new HashMap<Class<?>, Map<Class<?>, List<Field>>>();
69+
5670
// -- Class loading, querying and reflection --
5771

5872
/**
@@ -347,8 +361,12 @@ public static <A extends Annotation> List<Method> getAnnotatedMethods(
347361
public static <A extends Annotation> List<Field> getAnnotatedFields(
348362
final Class<?> c, final Class<A> annotationClass)
349363
{
350-
final ArrayList<Field> fields = new ArrayList<Field>();
364+
List<Field> fields = lookupFields(c, annotationClass);
365+
366+
if (fields != null) return fields;
367+
fields = new ArrayList<Field>();
351368
getAnnotatedFields(c, annotationClass, fields);
369+
352370
return fields;
353371
}
354372

@@ -383,6 +401,8 @@ public static <A extends Annotation> void getAnnotatedFields(
383401
final A ann = f.getAnnotation(annotationClass);
384402
if (ann != null) fields.add(f);
385403
}
404+
405+
mapFields(c, annotationClass, fields);
386406
}
387407

388408
/**
@@ -518,6 +538,45 @@ public static int compare(final Class<?> c1, final Class<?> c2) {
518538
return MiscUtils.compare(name1, name2);
519539
}
520540

541+
// -- Helper methods --
542+
543+
/**
544+
* Populates the provided list with {@link Field} entries of the given base
545+
* class which are annotated with the specified annotation type.
546+
*
547+
* @param c Base class
548+
* @param annotationClass Annotation type
549+
* @param annotatedFields Field list to populate
550+
*/
551+
private static <A extends Annotation> void mapFields(Class<?> c,
552+
Class<A> annotationClass, List<Field> annotatedFields)
553+
{
554+
Map<Class<?>, List<Field>> map = fields.get(c);
555+
if (map == null) {
556+
map = new HashMap<Class<?>, List<Field>>();
557+
fields.put(c, map);
558+
}
559+
560+
map.put(annotationClass, annotatedFields);
561+
}
562+
563+
/**
564+
* @param c Base class
565+
* @param annotationClass Annotation type
566+
* @return Cached list of Fields in the base class with the specified
567+
* annotation, or null if a cached list does not exist.
568+
*/
569+
private static <A extends Annotation> List<Field> lookupFields(
570+
final Class<?> c, final Class<A> annotationClass)
571+
{
572+
List<Field> annotatedFields = null;
573+
Map<Class<?>, List<Field>> annotationTypes = fields.get(c);
574+
if (annotationTypes != null) {
575+
annotatedFields = annotationTypes.get(annotationClass);
576+
}
577+
return annotatedFields;
578+
}
579+
521580
// -- Deprecated methods --
522581

523582
/** @deprecated use {@link ConversionUtils#convert(Object, Class)} */

0 commit comments

Comments
 (0)