parameterizedtype 上的附属注解是指直接标注在泛型类型参数位置(如 list 中的 @notblank)的注解,需通过 annotatedparameterizedtype 及其 getannotatedactualtypearguments() 获取,依赖 java 8+ 的 annotatedtype 体系和 runtime 保留策略。

什么是 ParameterizedType 上的附属注解?
Java 泛型在运行时会被擦除,但像 List<string></string>、Map<integer user></integer> 这类带具体类型参数的声明,在字段、方法参数或返回值上,仍可通过 ParameterizedType 获取原始类型和实际类型参数。而“附属注解”指的是直接写在泛型类型参数位置上的注解,比如:
public class Example {
private List names;
}
这里的 @NotBlank 并不在 String 类上,也不在字段上,而是附着在 String 作为 List 的类型参数这一角色上——它属于 ParameterizedType 中某个 TypeArgument 的 AnnotatedType。
核心思路:用 AnnotatedParameterizedType 替代 ParameterizedType
标准反射 API(如 Field.getGenericType())返回的是裸的 Type,不带注解信息。要读取泛型参数上的注解,必须使用 Java 8 引入的 AnnotatedType 体系:
- 获取字段/参数/返回值时,调用
Field.getAnnotatedType()、Parameter.getAnnotatedType()或Method.getAnnotatedReturnType() - 若该类型是参数化类型(如
List),则强转为AnnotatedParameterizedType - 再通过
getAnnotatedActualTypeArguments()获取每个泛型参数对应的AnnotatedType数组 - 对每个
AnnotatedType调用getAnnotations()或getAnnotation(Class)即可
完整代码示例(读取字段泛型参数上的注解)
假设有如下类:
public class Person {
private Map contacts;
}
读取 contacts 字段中两个泛型参数上的注解:
Field field = Person.class.getDeclaredField("contacts");
AnnotatedType annotatedType = field.getAnnotatedType();
if (annotatedType instanceof AnnotatedParameterizedType apType) {
AnnotatedType[] args = apType.getAnnotatedActualTypeArguments();
// 第一个参数:String,检查 @NotBlank
AnnotatedType keyType = args[0];
NotBlank keyNote = keyType.getAnnotation(NotBlank.class);
System.out.println("Key type has @NotBlank: " + (keyNote != null));
// 第二个参数:Address,检查 @NotNull 和 @Size
AnnotatedType valueType = args[1];
NotNull valueNotNull = valueType.getAnnotation(NotNull.class);
Size valueSize = valueType.getAnnotation(Size.class);
System.out.println("Value type has @NotNull: " + (valueNotNull != null));
System.out.println("Value type has @Size: " + (valueSize != null));
}
注意事项与常见坑
这个机制看似简单,但容易踩几个关键点:
-
仅适用于源码级注解:注解必须声明为
@Retention(RetentionPolicy.RUNTIME),且不能是CLASS或SOURCE - 编译器支持要求:需用 JDK 8+ 编译,并开启泛型注解支持(现代 javac 默认开启,但旧构建脚本可能禁用)
-
IDE 和字节码工具干扰:某些 Lombok、MapStruct 插件或混淆器会抹除
AnnotatedType信息,建议在纯编译输出上验证 -
嵌套泛型要递归处理:比如
List<map string integer>></map>,需对每个AnnotatedType判断是否为AnnotatedParameterizedType并深入











