自定义注解需三步:用@target和@retention声明作用位置与生命周期,用接口语法定义属性(支持string、基本类型、class、枚举、注解及数组),再通过反射或框架(如spring aop)读取并执行逻辑。

自定义注解不是“写个标签”就完事,它需要明确作用位置、生命周期和可配置项。核心在于三步:用元注解定范围与时机,用接口语法声明属性,再通过反射或框架机制读取并生效。
必须声明的两个元注解
每个自定义注解都得带上 @Target 和 @Retention,否则编译不通过。
-
@Target 指明能贴在哪——比如只允许标注方法,就写
@Target(ElementType.METHOD);若支持类、方法、字段,可写@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) -
@Retention 决定能活多久——业务逻辑需在运行时读取(如AOP拦截、日志打印),必须选
RetentionPolicy.RUNTIME;仅用于编译检查(如自定义 @NotNull),可用SOURCE
属性声明规则与默认值写法
注解里的“属性”本质是接口中的抽象方法,类型受限,但支持常见业务需求。
- 允许的类型包括:
String、基本类型(int、boolean等)、Class>、枚举、其他注解,以及这些类型的数组 - 每个属性后必须跟括号
(),有默认值就加default 值;无默认值则使用时必须显式赋值 - 若唯一需填的属性名为
value,调用时可省略名字,直接写@MyLog("保存用户") - 数组属性单元素可不写花括号,例如
roles = "ADMIN"等价于roles = {"ADMIN"}
一个带多属性的日志注解示例
假设要标记方法是否记录入参、操作类型和级别:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
String value() default ""; // 主描述,value名可省略
LogLevel level() default LogLevel.INFO; // 枚举类型属性
boolean recordParams() default true; // 布尔开关
String[] excludeFields() default {}; // 字符串数组,默认空
}
使用时可灵活组合:@Log("创建订单") —— 用默认值@Log(value = "删除缓存", recordParams = false) —— 显式覆盖部分值@Log(value = "权限校验", excludeFields = {"password", "token"}) —— 数组赋值
后续生效的关键:不只是定义,还要解析
定义完注解只是第一步。它本身不执行任何逻辑,必须配合反射或框架机制触发行为:
- 在 Spring 中,常结合
@Aspect切面,在@Around或@Before中用method.getAnnotation(Log.class)获取配置 - 手动反射解析时,先获取目标方法对象,再调用
getDeclaredAnnotation()或getAnnotation() - 注意:只有
@Retention(RetentionPolicy.RUNTIME)的注解才能被反射读到










