第41章

Java注解(Annotations)

深入学习Java注解的定义、使用和自定义,掌握元数据编程的核心概念和最佳实践

学习目标

什么是Java注解

注解(Annotation)是Java 5引入的重要特性,它提供了一种为代码添加元数据的方式。注解本身不会改变程序的执行逻辑,但可以被编译器、开发工具或运行时环境读取和处理。

注解的作用

Java内置注解

@Override

标记重写父类的方法,编译器会检查方法签名是否正确。

@Deprecated

标记已废弃的类、方法或字段,编译器会产生警告。

@SuppressWarnings

抑制编译器警告信息,如未检查的类型转换警告。

@SafeVarargs

标记安全的可变参数方法,抑制堆污染警告。

@FunctionalInterface

标记函数式接口,确保接口只有一个抽象方法。

内置注解使用示例
public class AnnotationBasics {
    
    // 标记已废弃的方法
    @Deprecated
    public void oldMethod() {
        System.out.println("已废弃的方法");
    }
    
    // 标记重写的方法
    @Override
    public String toString() {
        return "AnnotationBasics{}";
    }
    
    // 抑制编译器警告
    @SuppressWarnings("unchecked")
    public void methodWithWarnings() {
        java.util.List list = new java.util.ArrayList();
        list.add("Hello");
    }
    
    // 函数式接口
    @FunctionalInterface
    public interface Calculator {
        int calculate(int a, int b);
    }
}
💻 查看完整代码 - 在线IDE体验

自定义注解

创建自定义注解

自定义注解使用@interface关键字定义,可以包含属性和默认值。

自定义注解定义
import java.lang.annotation.*;

// 作者信息注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Author {
    String name() default "Unknown";
    String email() default "";
    String date() default "";
}

// 版本信息注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Version {
    double value() default 1.0;
    String description() default "";
}

// 测试方法注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
    String description() default "";
    boolean enabled() default true;
    int timeout() default 0;
}

元注解详解

@Retention

指定注解的生命周期:SOURCE、CLASS、RUNTIME

@Target

指定注解可以应用的目标:TYPE、FIELD、METHOD等

@Documented

标记注解是否包含在Javadoc中

@Inherited

标记注解是否可以被子类继承

反射读取注解

通过Java反射API可以在运行时读取和处理注解信息。

反射读取注解示例
public class CustomAnnotations {
    
    @Author(name = "张三", email = "zhangsan@example.com")
    @Version(value = 1.0, description = "初始版本")
    @Test(description = "测试计算功能", enabled = true)
    public int calculate(int a, int b) {
        return a + b;
    }
    
    public static void readAnnotations() throws Exception {
        Method method = CustomAnnotations.class.getMethod("calculate", int.class, int.class);
        
        // 读取Author注解
        if (method.isAnnotationPresent(Author.class)) {
            Author author = method.getAnnotation(Author.class);
            System.out.println("作者: " + author.name());
            System.out.println("邮箱: " + author.email());
        }
        
        // 读取Version注解
        if (method.isAnnotationPresent(Version.class)) {
            Version version = method.getAnnotation(Version.class);
            System.out.println("版本: " + version.value());
            System.out.println("描述: " + version.description());
        }
        
        // 读取Test注解
        if (method.isAnnotationPresent(Test.class)) {
            Test test = method.getAnnotation(Test.class);
            System.out.println("测试描述: " + test.description());
            System.out.println("是否启用: " + test.enabled());
        }
    }
}

注解处理器

注解处理器可以在编译时或运行时处理注解,实现各种功能如验证、日志、缓存等。

简单的验证注解处理器
// 验证注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Validate {
    String message() default "验证失败";
    boolean required() default false;
}

// 用户实体类
public class User {
    @Validate(required = true, message = "用户名不能为空")
    private String username;
    
    public User(String username) {
        this.username = username;
    }
}

// 验证处理器
public class ValidationProcessor {
    public static List validate(Object obj) {
        List errors = new ArrayList<>();
        try {
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Validate.class)) {
                    field.setAccessible(true);
                    Validate validate = field.getAnnotation(Validate.class);
                    Object value = field.get(obj);
                    
                    if (validate.required() && 
                        (value == null || value.toString().trim().isEmpty())) {
                        errors.add(validate.message());
                    }
                }
            }
        } catch (Exception e) {
            errors.add("验证错误: " + e.getMessage());
        }
        return errors;
    }
}

注解最佳实践

注解设计和使用原则

好的实践

  • 明确注解的目的和作用域
  • 提供合理的默认值
  • 使用有意义的名称
  • 添加完整的文档说明
  • 考虑性能影响

应该避免

  • 过度使用注解
  • 注解参数过多
  • 忽略性能开销
  • 缺乏文档说明
  • 不合理的生命周期设置

性能提示:反射操作有一定的性能开销,在高频调用的场景中应该考虑缓存注解信息,避免重复的反射操作。

实际应用场景

数据验证

使用注解标记字段验证规则,如@NotNull、@Size、@Email等

依赖注入

Spring框架的@Autowired、@Component、@Service等

ORM映射

JPA的@Entity、@Table、@Column等数据库映射注解

Web开发

Spring MVC的@Controller、@RequestMapping、@ResponseBody等

序列化

Jackson的@JsonProperty、@JsonIgnore等JSON序列化注解

单元测试

JUnit的@Test、@Before、@After等测试注解