!1434 perf(core): 为AnnotationUtil新增两级缓存架构,提升高频注解解析性能

Merge pull request !1434 from 07heco/perf/annotationutil-multilevel-cache
This commit is contained in:
Looly
2026-03-19 07:43:58 +00:00
committed by Gitee
2 changed files with 200 additions and 18 deletions

View File

@@ -9,8 +9,10 @@ import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.*;
import java.lang.annotation.*;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.AnnotatedElement;
@@ -41,6 +43,68 @@ public class AnnotationUtil {
Deprecated.class//
);
/**
* 注解查询两级缓存优化
* 哨兵对象用于表示“缓存中不存在该注解”避免缓存null导致NPE */
private static final Annotation NULL_ANNOTATION_SENTINEL = new Annotation() {
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
};
/**
* L1 原始注解缓存(核心高频场景)<br>
* 键:注解查询键(被注解元素 + 目标注解类型)<br>
* 值原生注解对象或NULL_ANNOTATION_SENTINEL表示不存在
*/
private static final WeakConcurrentMap<AnnotationLookupKey, Annotation> L1_ANNOTATION_CACHE = new WeakConcurrentMap<>();
/**
* L2 合成注解缓存(别名/聚合场景)<br>
* 键:注解查询键(被注解元素 + 目标注解类型)<br>
* 值合成注解对象或NULL_ANNOTATION_SENTINEL表示不存在
*/
private static final WeakConcurrentMap<AnnotationLookupKey, Annotation> L2_SYNTHESIZED_ANNOTATION_CACHE = new WeakConcurrentMap<>();
/**
* 注解查询缓存键,唯一标识一次注解查询操作
*/
private static class AnnotationLookupKey {
/** 被注解的元素Class、Method、Field等 */
private final AnnotatedElement element;
/** 目标注解类型 */
private final Class<? extends Annotation> annotationType;
/**
* 构造
*
* @param element 被注解的元素
* @param annotationType 目标注解类型
*/
public AnnotationLookupKey(AnnotatedElement element, Class<? extends Annotation> annotationType) {
this.element = element;
this.annotationType = annotationType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AnnotationLookupKey that = (AnnotationLookupKey) o;
return Objects.equals(element, that.element) && Objects.equals(annotationType, that.annotationType);
}
@Override
public int hashCode() {
return Objects.hash(element, annotationType);
}
}
/**
* 是否为Jdk自带的元注解。<br>
* 包括:
@@ -169,15 +233,34 @@ public class AnnotationUtil {
}
/**
* 获取指定注解
* 获取指定注解优化版新增L1缓存提升高频调用性能<br>
* 支持获取直接注解、元注解、组合注解,行为与原生实现完全一致
*
* @param <A> 注解类型
* @param annotationEle {@link AnnotatedElement}可以是Class、Method、Field、Constructor、ReflectPermission
* @param annotationType 注解类型
* @return 注解对象
* @return 注解对象,未找到返回{@code null}
*/
@SuppressWarnings("unchecked")
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotationEle, Class<A> annotationType) {
return (null == annotationEle) ? null : toCombination(annotationEle).getAnnotation(annotationType);
if (null == annotationEle || null == annotationType) {
return null;
}
// 优先从L1缓存获取
final AnnotationLookupKey key = new AnnotationLookupKey(annotationEle, annotationType);
Annotation cached = L1_ANNOTATION_CACHE.get(key);
// 缓存命中
if (null != cached) {
return (cached == NULL_ANNOTATION_SENTINEL) ? null : (A) cached;
}
// 缓存未命中:执行原生逻辑
final A result = toCombination(annotationEle).getAnnotation(annotationType);
// 存入缓存null值用哨兵代替
L1_ANNOTATION_CACHE.put(key, (null == result) ? NULL_ANNOTATION_SENTINEL : result);
return result;
}
/**
@@ -471,20 +554,35 @@ public class AnnotationUtil {
}
/**
* 获取别名支持后的注解
* 获取别名支持后的注解优化版新增L2缓存提升高频别名解析性能
*
* @param annotationEle 被注解的类
* @param annotationType 注解类型Class
* @param <T> 注解类型
* @return 别名支持后的注解
* @return 别名支持后的注解,未找到返回{@code null}
* @since 5.7.23
*/
@SuppressWarnings("unchecked")
public static <T extends Annotation> T getAnnotationAlias(AnnotatedElement annotationEle, Class<T> annotationType) {
final T annotation = getAnnotation(annotationEle, annotationType);
if (null == annotation) {
if (null == annotationEle || null == annotationType) {
return null;
}
return aggregatingFromAnnotation(annotation).synthesize(annotationType);
// 优先从L2缓存获取
final AnnotationLookupKey key = new AnnotationLookupKey(annotationEle, annotationType);
Annotation cached = L2_SYNTHESIZED_ANNOTATION_CACHE.get(key);
// 缓存命中
if (null != cached) {
return (cached == NULL_ANNOTATION_SENTINEL) ? null : (T) cached;
}
// 缓存未命中:执行原生逻辑
final T annotation = getAnnotation(annotationEle, annotationType);
final T result = (null == annotation) ? null : aggregatingFromAnnotation(annotation).synthesize(annotationType);
// 存入缓存null值用哨兵代替
L2_SYNTHESIZED_ANNOTATION_CACHE.put(key, (null == result) ? NULL_ANNOTATION_SENTINEL : result);
return result;
}
/**
@@ -506,7 +604,7 @@ public class AnnotationUtil {
}
/**
* <p>获取元素上距离指定元素最接近的合成注解
* <p>获取元素上距离指定元素最接近的合成注解优化版新增L2缓存避免重复合成解析
* <ul>
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
@@ -527,17 +625,38 @@ public class AnnotationUtil {
* @return 合成注解
* @see SynthesizedAggregateAnnotation
*/
@SuppressWarnings("unchecked")
public static <T extends Annotation> T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class<T> annotationType) {
T target = annotatedEle.getAnnotation(annotationType);
if (ObjectUtil.isNotNull(target)) {
return target;
if (null == annotatedEle || null == annotationType) {
return null;
}
return AnnotationScanner.DIRECTLY
.getAnnotationsIfSupport(annotatedEle).stream()
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
// 优先从L2缓存获取
final AnnotationLookupKey key = new AnnotationLookupKey(annotatedEle, annotationType);
Annotation cached = L2_SYNTHESIZED_ANNOTATION_CACHE.get(key);
// 缓存命中
if (null != cached) {
return (cached == NULL_ANNOTATION_SENTINEL) ? null : (T) cached;
}
// 缓存未命中:执行原生逻辑
T result = annotatedEle.getAnnotation(annotationType);
if (ObjectUtil.isNotNull(result)) {
L2_SYNTHESIZED_ANNOTATION_CACHE.put(key, result);
return result;
}
result = AnnotationScanner.DIRECTLY
.getAnnotationsIfSupport(annotatedEle).stream()
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
// 存入缓存null值用哨兵代替
L2_SYNTHESIZED_ANNOTATION_CACHE.put(key, (null == result) ? NULL_ANNOTATION_SENTINEL : result);
return result;
}
/**

View File

@@ -164,4 +164,67 @@ public class AnnotationUtilTest {
@AnnotationForTest("SuperInterface")
interface SuperInterface{}
/**
* 两级缓存优化专项测试
* 测试L1缓存getAnnotation方法缓存命中功能正常
*/
@Test
public void AnnotationL1CacheTest() {
// 1. 复用已有测试类:直接注解场景
final AnnotationForTest annotation1 = AnnotationUtil.getAnnotation(ClassWithAnnotation.class, AnnotationForTest.class);
assertNotNull(annotation1);
// 验证缓存命中:第二次调用返回同一个对象
final AnnotationForTest annotation2 = AnnotationUtil.getAnnotation(ClassWithAnnotation.class, AnnotationForTest.class);
assertSame(annotation1, annotation2, "L1缓存-直接注解场景未命中");
// 2. 复用已有测试类:层级扫描场景
final AnnotationForTest hierarchyAnnotation1 = AnnotationUtil.getAnnotation(TargetClass.class, AnnotationForTest.class);
assertNotNull(hierarchyAnnotation1);
// 验证缓存命中
final AnnotationForTest hierarchyAnnotation2 = AnnotationUtil.getAnnotation(TargetClass.class, AnnotationForTest.class);
assertSame(hierarchyAnnotation1, hierarchyAnnotation2, "L1缓存-层级扫描场景未命中");
// 3. 边界场景不存在的注解返回null
final Deprecated nullAnnotation = AnnotationUtil.getAnnotation(ClassWithAnnotation.class, Deprecated.class);
assertNull(nullAnnotation);
}
/**
* 测试L2缓存getAnnotationAlias方法缓存命中功能正常
*/
@Test
public void AnnotationAliasL2CacheTest() {
// 复用已有测试类:自带@AliasFor别名逻辑无需自定义注解
final AnnotationForTest annotation1 = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
assertNotNull(annotation1);
// 验证缓存命中:第二次调用返回同一个对象
final AnnotationForTest annotation2 = AnnotationUtil.getAnnotationAlias(ClassWithAnnotation.class, AnnotationForTest.class);
assertSame(annotation1, annotation2, "L2缓存-getAnnotationAlias场景未命中");
// 验证功能正常:别名值正确(原有测试已覆盖别名逻辑)
assertTrue(annotation1.retry().equals("测试") || annotation1.retry().equals("repeat-annotation"));
}
/**
* 测试L2缓存getSynthesizedAnnotation方法缓存命中功能正常
*/
@Test
public void SynthesizedAnnotationL2CacheTest() {
// 复用已有元注解测试类
final RootMetaAnnotation1 metaAnnotation1 = AnnotationUtil.getSynthesizedAnnotation(RootAnnotation.class, RootMetaAnnotation1.class);
assertNotNull(metaAnnotation1);
// 验证缓存命中
final RootMetaAnnotation1 metaAnnotation2 = AnnotationUtil.getSynthesizedAnnotation(RootAnnotation.class, RootMetaAnnotation1.class);
assertSame(metaAnnotation1, metaAnnotation2, "L2缓存-getSynthesizedAnnotation场景未命中");
}
/**
* 测试依赖方法hasAnnotation方法因L1缓存受益功能正常
*/
@Test
public void HasAnnotationWithCacheTest() {
assertTrue(AnnotationUtil.hasAnnotation(ClassWithAnnotation.class, AnnotationForTest.class));
assertTrue(AnnotationUtil.hasAnnotation(TargetClass.class, AnnotationForTest.class));
assertFalse(AnnotationUtil.hasAnnotation(ClassWithAnnotation.class, Deprecated.class));
}
}