Java注解(Annotation)全面学习指南
一、注解基础概念
1.1 什么是注解
- 代码级别的元数据(metadata)
- 为代码添加信息标签,不影响程序逻辑
- JDK 1.5引入的特性
1.2 注解的作用
- 编译检查(如@Override)
- 代码分析(生成文档、代码分析)
- 编译时和运行时处理
- 框架配置(如Spring、JPA)
二、内置注解
2.1 编译相关注解
java
// 1. @Override - 检查方法重写
@Override
public String toString() {
return "This is overridden method";
}
// 2. @Deprecated - 标记已过时
@Deprecated
public void oldMethod() {
// 不推荐使用
}
// 3. @SuppressWarnings - 抑制警告
@SuppressWarnings("unchecked")
List<String> list = new ArrayList();
// 4. @FunctionalInterface - 函数式接口(Java 8+)
@FunctionalInterface
interface MyFunctionalInterface {
void execute();
}
2.2 元注解(用于定义注解的注解)
java
import java.lang.annotation.*;
// 1. @Target - 指定注解使用位置
@Target({
ElementType.TYPE, // 类、接口、枚举
ElementType.FIELD, // 字段
ElementType.METHOD, // 方法
ElementType.PARAMETER, // 参数
ElementType.CONSTRUCTOR,// 构造器
ElementType.LOCAL_VARIABLE, // 局部变量
ElementType.ANNOTATION_TYPE, // 注解类型
ElementType.PACKAGE, // 包
ElementType.TYPE_PARAMETER, // 类型参数(Java 8+)
ElementType.TYPE_USE // 类型使用(Java 8+)
})
// 2. @Retention - 指定注解保留策略
@Retention(RetentionPolicy.SOURCE) // 仅源码保留
@Retention(RetentionPolicy.CLASS) // 编译时保留(默认)
@Retention(RetentionPolicy.RUNTIME) // 运行时保留(可通过反射获取)
// 3. @Documented - 包含在Javadoc中
@Documented
// 4. @Inherited - 允许子类继承
@Inherited
// 5. @Repeatable - 可重复注解(Java 8+)
@Repeatable
三、自定义注解
3.1 基本语法
java
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 注解元素(看起来像方法,实际上是属性)
String value() default "default"; // 默认值
int count() default 0;
String[] tags() default {};
Class<?> clazz() default Object.class;
RetentionPolicy policy() default RetentionPolicy.RUNTIME;
}
// 使用注解
public class AnnotationDemo {
@MyAnnotation(value = "test", count = 5, tags = {"java", "annotation"})
public void myMethod() {
// 方法体
}
}
3.2 注解元素类型限制
- 基本数据类型(int, float, boolean等)
- String
- Class
- Enum
- Annotation
- 以上类型的数组
不允许:null作为默认值
四、注解处理
4.1 运行时处理(反射)
java
import java.lang.reflect.*;
// 定义运行时注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
String name();
String version() default "1.0";
}
// 使用注解
@CustomAnnotation(name = "DemoClass", version = "2.0")
class DemoClass {
@CustomAnnotation(name = "demoMethod")
public void demoMethod() {}
}
// 处理注解
public class AnnotationProcessor {
public static void main(String[] args) {
// 类注解处理
Class<DemoClass> clazz = DemoClass.class;
// 检查类是否有注解
if (clazz.isAnnotationPresent(CustomAnnotation.class)) {
CustomAnnotation annotation = clazz.getAnnotation(CustomAnnotation.class);
System.out.println("Class: " + annotation.name());
System.out.println("Version: " + annotation.version());
}
// 方法注解处理
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(CustomAnnotation.class)) {
CustomAnnotation annotation = method.getAnnotation(CustomAnnotation.class);
System.out.println("Method: " + method.getName());
System.out.println("Annotation name: " + annotation.name());
}
}
// 获取所有注解(包括继承的)
Annotation[] annotations = clazz.getAnnotations();
// 获取直接声明的注解
Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
}
}
4.2 编译时处理(APT - Annotation Processing Tool)
java
// 1. 定义Processor
@SupportedAnnotationTypes({"com.example.*"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// 处理注解逻辑
for (TypeElement annotation : annotations) {
Set<? extends Element> elements =
roundEnv.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
// 生成代码或执行检查
processingEnv.getMessager().printMessage(
Diagnostic.Kind.NOTE,
"Found annotation on: " + element.getSimpleName()
);
}
}
return true;
}
}
五、高级特性
5.1 重复注解(Java 8+)
java
// 1. 定义容器注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Authors {
Author[] value();
}
// 2. 定义可重复注解
@Repeatable(Authors.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Author {
String name();
String date();
}
// 3. 使用
@Author(name = "Alice", date = "2024-01-01")
@Author(name = "Bob", date = "2024-01-02")
class Book {
// 类定义
}
5.2 类型注解(Java 8+)
java
// 类型参数注解
class TypeAnnotationDemo<@NotEmpty T> {
// 类型使用注解
List<@NotEmpty String> list;
// 方法引用注解
void process(@NotNull String input) {
// 类型转换注解
String str = (@NotNull String) obj;
}
}
5.3 注解继承
java
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface InheritedAnnotation {
String value();
}
@InheritedAnnotation("parent")
class ParentClass {}
// ChildClass会自动继承@InheritedAnnotation
class ChildClass extends ParentClass {}
六、实际应用示例
6.1 自定义验证注解
java
// 定义验证注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Validation {
int min() default 0;
int max() default Integer.MAX_VALUE;
boolean required() default false;
String regex() default "";
}
// 使用验证注解
class User {
@Validation(required = true, min = 2, max = 50)
private String username;
@Validation(required = true, regex = "^(?=.*[A-Za-z])(?=.*\\d).{8,}$")
private String password;
@Validation(min = 18, max = 100)
private int age;
}
// 验证器
class Validator {
public static List<String> validate(Object obj) throws IllegalAccessException {
List<String> errors = new ArrayList<>();
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(Validation.class)) {
Validation validation = field.getAnnotation(Validation.class);
Object value = field.get(obj);
if (validation.required() && value == null) {
errors.add(field.getName() + " is required");
}
if (value instanceof String) {
String str = (String) value;
if (validation.required() && str.trim().isEmpty()) {
errors.add(field.getName() + " cannot be empty");
}
if (str.length() < validation.min()) {
errors.add(field.getName() + " is too short");
}
if (str.length() > validation.max()) {
errors.add(field.getName() + " is too long");
}
if (!validation.regex().isEmpty() && !str.matches(validation.regex())) {
errors.add(field.getName() + " format is invalid");
}
}
if (value instanceof Integer) {
int num = (int) value;
if (num < validation.min()) {
errors.add(field.getName() + " is too small");
}
if (num > validation.max()) {
errors.add(field.getName() + " is too large");
}
}
}
}
return errors;
}
}
6.2 日志注解
java
// 日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface LogExecution {
String level() default "INFO";
boolean logParams() default true;
boolean logResult() default false;
}
// AOP处理(使用Spring AOP示例)
@Aspect
@Component
class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Around("@annotation(logExecution)")
public Object logMethod(ProceedingJoinPoint joinPoint, LogExecution logExecution)
throws Throwable {
String methodName = joinPoint.getSignature().getName();
// 记录参数
if (logExecution.logParams()) {
Object[] args = joinPoint.getArgs();
logger.log(Level.parse(logExecution.level()),
"Method {} called with params: {}",
methodName, Arrays.toString(args));
}
// 执行方法
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
// 记录结果
if (logExecution.logResult()) {
logger.log(Level.parse(logExecution.level()),
"Method {} returned: {}, execution time: {}ms",
methodName, result, endTime - startTime);
} else {
logger.log(Level.parse(logExecution.level()),
"Method {} executed in {}ms",
methodName, endTime - startTime);
}
return result;
}
}
七、常见框架中的注解
7.1 Spring Framework
java
// 常用Spring注解
@Component // 通用组件
@Repository // 数据访问层
@Service // 业务层
@Controller // 控制层
@RestController // REST控制器
@Autowired // 自动注入
@Qualifier // 指定Bean名称
@Value // 注入值
@RequestMapping // 请求映射
@GetMapping // GET请求
@PostMapping // POST请求
@RequestBody // 请求体
@ResponseBody // 响应体
7.2 JPA/Hibernate
java
@Entity // 实体类
@Table // 表映射
@Id // 主键
@GeneratedValue // 主键生成策略
@Column // 列映射
@OneToMany // 一对多
@ManyToOne // 多对一
@ManyToMany // 多对多
八、最佳实践
8.1 设计原则
- 单一职责:每个注解应该有明确单一的目的
- 命名清晰:使用明确、描述性的名称
- 合理默认值:为可选参数提供合理的默认值
- 文档完善:使用@Documented和JavaDoc
8.2 性能考虑
- 运行时注解通过反射获取,可能有性能开销
- 频繁使用的注解应考虑缓存
- 编译时注解无运行时开销
8.3 兼容性
- 考虑注解的继承性
- 避免破坏性更改
- 提供合理的默认值以保持向后兼容
九、学习资源
9.1 官方文档
9.2 实践建议
- 从使用内置注解开始
- 尝试自定义简单注解
- 学习使用反射处理注解
- 研究主流框架的注解实现
- 实践编译时注解处理
十、总结
Java注解是现代Java开发的核心特性,广泛应用于:
- 框架配置和元数据
- 代码生成和检查
- AOP编程
- 测试框架
- 序列化和验证
掌握注解的使用和自定义,能够显著提升代码质量和开发效率。建议结合实际项目需求,逐步深入学习和应用。