1. 什么是注解
注解(Annotation)是 Java 5 引入的一种元数据机制,它允许我们在代码中添加结构化、机器可读的元数据信息。注解本身不直接影响程序运行逻辑,但可以被编译器、框架和工具在编译期或运行期读取并处理。
简单来说,注解就是给代码贴的「标签」,这些标签可以被各种工具识别并触发相应的行为。
2. 注解的分类
Java 注解按生命周期和作用范围可以分为以下几类:
2.1 按生命周期分类
- 源码级注解(SOURCE):仅在源码中存在,编译时被丢弃。典型代表是
@Override、@SuppressWarnings。 - 编译期注解(CLASS):保留到编译后的 class 文件中,但运行时不可见。常用于字节码增强工具。
- 运行期注解(RUNTIME):不仅保留在 class 文件中,还能在运行时通过反射读取。这是框架开发中最常用的一类,如
@Autowired、@RequestMapping。
2.2 按用途分类
- 标记注解:不含任何成员变量,仅用于标记,如
@Deprecated。 - 单值注解:只有一个成员变量,通常命名为
value,使用时可以省略变量名,如@SuppressWarnings("unchecked")。 - 完整注解:包含多个成员变量,使用时需显式指定,如
@RequestMapping(value = "/user", method = RequestMethod.GET)。
3. 自定义注解
3.1 定义注解
使用@interface关键字定义注解:
importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public@interfaceLogExecutionTime{Stringvalue()default"";booleanprintArgs()defaultfalse;}3.2 注解成员变量
注解的成员变量以方法的形式声明,可以指定默认值:
public@interfaceApiInfo{Stringauthor()default"unknown";Stringversion()default"1.0.0";String[]tags()default{};}成员变量支持的类型包括:基本类型、String、Class、枚举、注解以及这些类型的数组。
4. 元注解
元注解是用于修饰其他注解的注解,Java 提供了以下四种核心元注解:
| 元注解 | 作用 |
|---|---|
@Retention | 指定注解的生命周期(SOURCE/CLASS/RUNTIME) |
@Target | 指定注解可以修饰的目标(类、方法、字段等) |
@Documented | 标记该注解应被 javadoc 文档化 |
@Inherited | 标记该注解可被子类继承 |
4.1 @Target 的可选值
ElementType.TYPE// 类、接口、枚举ElementType.FIELD// 字段ElementType.METHOD// 方法ElementType.PARAMETER// 参数ElementType.CONSTRUCTOR// 构造器ElementType.LOCAL_VARIABLE// 局部变量ElementType.ANNOTATION_TYPE// 注解类型ElementType.PACKAGE// 包ElementType.TYPE_PARAMETER// 类型参数ElementType.TYPE_USE// 类型使用处5. 注解的读取
5.1 运行时反射读取
importjava.lang.reflect.Method;publicclassAnnotationDemo{@LogExecutionTime(value="getUser",printArgs=true)publicvoidgetUser(Stringid){System.out.println("查询用户:"+id);}publicstaticvoidmain(String[]args)throwsException{Methodmethod=AnnotationDemo.class.getMethod("getUser",String.class);if(method.isAnnotationPresent(LogExecutionTime.class)){LogExecutionTimeannotation=method.getAnnotation(LogExecutionTime.class);System.out.println("注解值:"+annotation.value());System.out.println("是否打印参数:"+annotation.printArgs());}}}5.2 编译期处理(APT)
编译期注解处理器通过javax.annotation.processing包实现,常用于生成代码,如 Lombok、MapStruct 等框架。
6. 注解的典型应用场景
6.1 框架开发
Spring、MyBatis 等框架大量使用注解简化配置:
@RestController@RequestMapping("/api/users")publicclassUserController{@GetMapping("/{id}")publicUsergetUser(@PathVariableLongid){returnuserService.findById(id);}}6.2 代码生成
通过注解处理器在编译期自动生成样板代码,减少手写工作量。
6.3 参数校验
publicclassUser{@NotNull(message="用户名不能为空")@Size(min=2,max=20,message="用户名长度必须在 2-20 之间")privateStringname;}7. 注解与反射的配合
注解本身只是元数据,真正发挥作用需要配合反射或注解处理器。运行时注解的典型处理流程如下:
8. 注意事项
- 注解成员变量不能使用包装类型,必须使用基本类型。
- 注解不能继承其他注解,但可以通过元注解
@Inherited实现类级别的继承效果。 - 运行时注解的反射读取有一定性能开销,高频调用场景需谨慎使用。
- 注解成员变量名不能使用
element等保留关键字。
9. 总结
Java 注解是一种强大的元数据机制,它让代码更加简洁、可读性更强,也是现代 Java 框架的基石。掌握注解的定义、元注解的使用以及反射读取方式,是深入理解 Spring 等主流框架的必备技能。建议读者动手实践一个自定义注解,结合反射实现一个简单的日志切面,加深理解。