Java注解不支持继承,解决重复声明的核心是组合与运行时处理:统一元注解、接口抽象共用属性、容器注解打包、动态代理注入逻辑。

Java 注解默认不支持继承(即不能用 @interface 继承另一个注解),这导致在需要复用注解逻辑的场景下,容易出现大量重复声明——比如多个自定义校验注解都需配置 message、groups、payload 等通用属性。解决的核心思路不是“让注解继承”,而是绕过语言限制,通过组合、抽象和运行时处理来消除重复。
统一提取通用元注解(@Retention/@Target等)
虽然业务注解本身不能继承,但它们可以**统一标注相同的元注解**。把重复的元信息(如 @Retention(RetentionPolicy.RUNTIME)、@Target({ElementType.FIELD, ElementType.PARAMETER}))抽成一个公共模板,所有自定义注解都显式加上——这不是继承,但能保证行为一致,减少手误。
- 定义一个空的标记型元注解(如
@CommonConstraint),只包含@Retention和@Target - 所有校验注解(如
@EmailValid、@PhoneValid)都直接标注@CommonConstraint - 后续若需调整作用域或保留策略,只需改一处
用接口抽象共用属性 + 工具类封装解析逻辑
将注解中重复的字段(如 String message() default ""、Class>[] groups() default {})提取为普通 Java 接口(非注解),再让各注解在声明时“手动实现”该接口的语义——虽不能强制实现,但可通过工具类统一读取:
- 定义接口
ConstraintDescriptor,含message()、groups()等方法签名 - 每个自定义注解都按约定提供同名方法(IDE 可用 Live Template 快速生成)
- 编写通用解析器
ConstraintUtils.getErrorMessage(Annotation ann, Object value),用反射调用这些方法 - 校验逻辑不再耦合具体注解类型,只依赖接口契约
基于注解容器统一管理(@Constraints)
当多个注解常一起使用(如字段既要非空又要长度校验),可定义一个“容器注解”,把常用组合打包,避免每次重复写一堆:
- 定义
@NotBlankAndSize(min=2, max=20),内部包含@NotBlank和@Size的配置 - 配合自定义
ConstraintValidator,在isValid()中分别调用两个校验器 - Spring Boot 2.5+ 支持
@Repeatable和容器注解自动识别,无需额外配置 - 既保持语义清晰,又减少 XML 或 Java Config 中的重复声明
运行时动态代理注解(高级方案)
对强复用需求(如跨模块共享校验规则),可用字节码或 JDK 动态代理,在运行时生成“虚拟注解实例”,把通用逻辑注入到不同注解对象中:
- 用
java.lang.reflect.Proxy创建代理对象,拦截message()等方法调用 - 代理背后统一查配置中心或 Properties,实现 message 多语言/环境化
- 适合中台或框架层,业务代码无需感知,但需注意代理对象与原始注解类型不等价(
ann.getClass() != EmailValid.class)

















