在 Java 中,自定义注解是一种定义新类型的注解的方式。注解是一种元数据,能够提供关于代码的信息,并且可以在编译时或运行时通过反射来进行处理。自定义注解的作用主要是给代码添加元数据,这些元数据可以通过反射机制读取,并用于代码的其他方面,比如依赖注入、AOP(面向切面编程)等。

自定义注解的基本步骤

  1. 定义注解: Java 中定义注解的方式是使用 @interface 关键字。例如,创建一个简单的注解 @MyAnnotation
  2. 使用注解: 定义完注解后,可以在代码中应用这个注解,注解可以加在类、方法、字段等地方,具体取决于注解的元注解配置。
  3. 读取注解: 使用反射读取注解的值,通常通过 Annotation 接口来操作。

自定义注解的步骤

1. 定义注解

使用 @interface 定义一个注解类型。注解可以包含元素(即属性),但注解本身不能有构造函数。

public @interface MyAnnotation {
    String value() default "default value";  // 一个元素,设置默认值
    int number() default 1;  // 一个整数元素,设置默认值
}

2. 使用注解

注解可以应用在类、方法、字段、参数等地方。注解可以带有属性,也可以不带属性。

@MyAnnotation(value = "Hello", number = 42)
public class MyClass {
    @MyAnnotation(value = "Test")
    public void myMethod() {
        // 方法内容
    }
}

3. 元注解(Meta-annotation)

元注解是用来注解其他注解的注解。Java 提供了几种元注解,用来控制注解的使用范围。

  • @Retention:指定注解的保留策略,定义注解在哪个阶段可用。
  • RetentionPolicy.SOURCE:注解只保留在源代码中,编译后丢弃。
  • RetentionPolicy.CLASS:注解在编译后保留在字节码文件中,但在运行时不可用。
  • RetentionPolicy.RUNTIME:注解在运行时仍然存在,可以通过反射读取。
  • @Target:指定注解可以应用于哪些 Java 元素(类、方法、字段、参数等)。
  • ElementType.TYPE:类、接口、枚举
  • ElementType.FIELD:字段
  • ElementType.METHOD:方法
  • ElementType.PARAMETER:参数
  • ElementType.LOCAL_VARIABLE:局部变量
  • ElementType.ANNOTATION_TYPE:注解类型
  • @Inherited:指示一个注解可以被子类继承。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyMethodAnnotation {
    String description() default "No description";
}

4. 读取注解

通过 Java 反射,可以读取应用到类、方法、字段上的注解。注解可以通过 getAnnotation() 方法获取,反射 API 可以用来获取注解的具体值。

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class AnnotationReader {
    public static void main(String[] args) throws Exception {
        Method method = MyClass.class.getMethod("myMethod");
        if (method.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
            System.out.println("Value: " + annotation.value());
            System.out.println("Number: " + annotation.number());
        }
    }
}

5. 注解的应用实例

假设我们有一个简单的场景:需要记录方法执行的时间,可以自定义一个注解来标记需要记录的函数。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
    // 不需要任何元素,只是用来标记
}

然后,在实际使用时,通过反射动态处理注解,记录方法执行时间。

public class MethodTimer {
    public static void main(String[] args) throws Exception {
        Method method = MethodTimer.class.getMethod("exampleMethod");
        if (method.isAnnotationPresent(LogExecutionTime.class)) {
            long start = System.nanoTime();
            method.invoke(new MethodTimer());
            long end = System.nanoTime();
            System.out.println("Method executed in: " + (end - start) + " nanoseconds.");
        }
    }

    @LogExecutionTime
    public void exampleMethod() {
        // 模拟方法执行
        try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
    }
}