mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Avoid unnecessary allocations for cached annotation mappings
This commit defers creation of the visited annotation types set until a cache miss occurs, which avoids allocating a HashSet for every cached annotation mapping lookup while preserving recursive annotation handling during mapping creation. Closes gh-37141 Signed-off-by: GT <gregjotau@gmail.com>
This commit is contained in:
+35
-9
@@ -46,6 +46,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @author Greg Taube
|
||||
* @since 5.2
|
||||
* @see AnnotationTypeMapping
|
||||
*/
|
||||
@@ -178,7 +179,7 @@ final class AnnotationTypeMappings {
|
||||
* @return type mappings for the annotation type
|
||||
*/
|
||||
static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType) {
|
||||
return forAnnotationType(annotationType, new HashSet<>());
|
||||
return forAnnotationType(annotationType, RepeatableContainers.standardRepeatables(), AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +209,11 @@ final class AnnotationTypeMappings {
|
||||
static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType,
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
return forAnnotationType(annotationType, repeatableContainers, annotationFilter, new HashSet<>());
|
||||
Cache cache = getCache(repeatableContainers, annotationFilter);
|
||||
if (cache != null) {
|
||||
return cache.get(annotationType);
|
||||
}
|
||||
return new AnnotationTypeMappings(repeatableContainers, annotationFilter, annotationType, new HashSet<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,18 +232,28 @@ final class AnnotationTypeMappings {
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter,
|
||||
Set<Class<? extends Annotation>> visitedAnnotationTypes) {
|
||||
|
||||
if (repeatableContainers == RepeatableContainers.standardRepeatables()) {
|
||||
return standardRepeatablesCache.computeIfAbsent(annotationFilter,
|
||||
key -> new Cache(repeatableContainers, key)).get(annotationType, visitedAnnotationTypes);
|
||||
}
|
||||
if (repeatableContainers == RepeatableContainers.none()) {
|
||||
return noRepeatablesCache.computeIfAbsent(annotationFilter,
|
||||
key -> new Cache(repeatableContainers, key)).get(annotationType, visitedAnnotationTypes);
|
||||
Cache cache = getCache(repeatableContainers, annotationFilter);
|
||||
if (cache != null) {
|
||||
return cache.get(annotationType, visitedAnnotationTypes);
|
||||
}
|
||||
return new AnnotationTypeMappings(repeatableContainers, annotationFilter, annotationType,
|
||||
visitedAnnotationTypes);
|
||||
}
|
||||
|
||||
private static @Nullable Cache getCache(
|
||||
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
|
||||
|
||||
if (repeatableContainers == RepeatableContainers.standardRepeatables()) {
|
||||
return standardRepeatablesCache.computeIfAbsent(annotationFilter,
|
||||
key -> new Cache(repeatableContainers, key));
|
||||
}
|
||||
if (repeatableContainers == RepeatableContainers.none()) {
|
||||
return noRepeatablesCache.computeIfAbsent(annotationFilter,
|
||||
key -> new Cache(repeatableContainers, key));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void clearCache() {
|
||||
standardRepeatablesCache.clear();
|
||||
noRepeatablesCache.clear();
|
||||
@@ -277,6 +292,17 @@ final class AnnotationTypeMappings {
|
||||
AnnotationTypeMappings get(Class<? extends Annotation> annotationType,
|
||||
Set<Class<? extends Annotation>> visitedAnnotationTypes) {
|
||||
|
||||
return getOrCreate(annotationType, visitedAnnotationTypes);
|
||||
}
|
||||
|
||||
AnnotationTypeMappings get(Class<? extends Annotation> annotationType) {
|
||||
AnnotationTypeMappings result = this.mappings.get(annotationType);
|
||||
return (result != null ? result : getOrCreate(annotationType, new HashSet<>()));
|
||||
}
|
||||
|
||||
private AnnotationTypeMappings getOrCreate(Class<? extends Annotation> annotationType,
|
||||
Set<Class<? extends Annotation>> visitedAnnotationTypes) {
|
||||
|
||||
AnnotationTypeMappings result = this.mappings.get(annotationType);
|
||||
if (result != null) {
|
||||
return result;
|
||||
|
||||
+7
@@ -47,6 +47,13 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*/
|
||||
class AnnotationTypeMappingsTests {
|
||||
|
||||
@Test
|
||||
void forAnnotationTypeWhenCalledTwiceReturnsCachedInstance() {
|
||||
AnnotationTypeMappings first = AnnotationTypeMappings.forAnnotationType(SimpleAnnotation.class);
|
||||
AnnotationTypeMappings second = AnnotationTypeMappings.forAnnotationType(SimpleAnnotation.class);
|
||||
assertThat(second).isSameAs(first);
|
||||
}
|
||||
|
||||
@Test
|
||||
void forAnnotationTypeWhenNoMetaAnnotationsReturnsMappings() {
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(SimpleAnnotation.class);
|
||||
|
||||
Reference in New Issue
Block a user