Java注解基础
Java注解基础
一、注解(Annotation)基础概念
注解是 Java 5 引入的代码元数据形式,通过 @interface
关键字定义。它们本身不直接影响代码逻辑,但通过反射机制可以被编译器、运行时或其他工具处理。典型应用场景包括:
- 框架配置(如 Spring 的
@Controller
) - 代码生成(如 Lombok 的
@Data
) - 编译时检查(如
@Override
)
二、元注解(Meta-Annotation)核心解析
元注解是专门用于定义其他注解的注解,共有 5 种标准元注解。理解它们是掌握自定义注解的关键。
1. @Target
:指定注解作用目标
作用:控制注解可以应用在哪些程序元素上
参数:ElementType[]
数组,常用值:
TYPE
:类、接口、枚举FIELD
:字段(包括枚举常量)METHOD
:方法PARAMETER
:方法参数CONSTRUCTOR
:构造函数LOCAL_VARIABLE
:局部变量ANNOTATION_TYPE
:注解类型本身PACKAGE
:包声明
示例:
@Target(ElementType.METHOD) // 仅作用于方法
public @interface Loggable {
String value() default "";
}
2. @Retention
:定义注解生命周期
作用:控制注解被保留到哪个阶段
参数:RetentionPolicy
枚举:
SOURCE
:仅保留在源码中,编译时丢弃(如@Override
)CLASS
:保留到字节码文件,但运行时不可见(默认)RUNTIME
:保留到运行时,可通过反射读取
示例:
@Retention(RetentionPolicy.RUNTIME) // 运行时可见
public @interface Audit {
String operator() default "SYSTEM";
}
3. @Documented
:生成文档时包含注解
作用:使用 javadoc 生成 API 文档时,保留注解信息
示例:
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
String version();
}
4. @Inherited
:允许子类继承父类注解
作用:当注解作用在类上时,子类自动继承该注解
注意:仅对类注解有效,对方法/字段注解无效
示例:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface DeprecatedApi {
String replacement() default "";
}
@DeprecatedApi(replacement = "NewService")
class OldService {}
class NewService extends OldService {} // 自动继承 @DeprecatedApi
5. @Repeatable
(Java 8+):允许重复应用
作用:解决同一注解不能重复作用于同一目标的问题
实现:需配合容器注解使用
示例:
// 定义可重复的注解
@Repeatable(Authorities.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authority {
String role();
}
// 容器注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorities {
Authority[] value();
}
// 使用示例
@Authority("admin")
@Authority("user")
class UserController {}
三、自定义注解实战案例
场景:实现方法执行时间统计
- 定义注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeLog {
String description() default "";
}
- 通过 AOP 实现切面(需 Spring AOP 或 AspectJ):
@Aspect
@Component
public class TimeLogAspect {
@Around("@annotation(timeLog)")
public Object logTime(ProceedingJoinPoint joinPoint, TimeLog timeLog) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
System.out.printf("%s 执行耗时: %d ms%n",
timeLog.description(), duration);
return result;
}
}
- 使用注解:
@Service
public class OrderService {
@TimeLog(description = "创建订单操作")
public void createOrder(Order order) {
// 业务逻辑
}
}
四、最佳实践建议
- 明确生命周期:根据需求选择
SOURCE
/CLASS
/RUNTIME
- 精准定位目标:避免过度使用
ElementType.TYPE
- 提供默认值:简化注解使用(如
String value() default ""
) - 组合元注解:通过组合实现复杂行为(如
@Documented + @Inherited
) - 防御性编程:对注解参数进行空值检查