深入学习Java注解的定义、使用和自定义,掌握元数据编程的核心概念和最佳实践
注解(Annotation)是Java 5引入的重要特性,它提供了一种为代码添加元数据的方式。注解本身不会改变程序的执行逻辑,但可以被编译器、开发工具或运行时环境读取和处理。
标记重写父类的方法,编译器会检查方法签名是否正确。
标记已废弃的类、方法或字段,编译器会产生警告。
抑制编译器警告信息,如未检查的类型转换警告。
标记安全的可变参数方法,抑制堆污染警告。
标记函数式接口,确保接口只有一个抽象方法。
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);
}
}
自定义注解使用@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;
}
指定注解的生命周期:SOURCE、CLASS、RUNTIME
指定注解可以应用的目标:TYPE、FIELD、METHOD等
标记注解是否包含在Javadoc中
标记注解是否可以被子类继承
通过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等
JPA的@Entity、@Table、@Column等数据库映射注解
Spring MVC的@Controller、@RequestMapping、@ResponseBody等
Jackson的@JsonProperty、@JsonIgnore等JSON序列化注解
JUnit的@Test、@Before、@After等测试注解