杰瑞科技汇

java annotation 自定义

  1. 什么是注解?
  2. 自定义注解的基本语法
  3. 元注解
  4. 如何使用自定义注解(通过反射)
  5. 一个完整的实战案例:一个简单的日志切面
  6. 注解的保留策略和目标

什么是注解?

注解是 Java 5 引入的一种特殊接口,它附在代码(如类、方法、字段、参数等)之上,提供关于代码的额外信息,它本身不执行任何操作,就像一个标签,标记代码的某些特性,其他工具(如编译器、IDE、框架)可以读取这些标签,并根据其定义的逻辑来执行相应的操作。

java annotation 自定义-图1
(图片来源网络,侵删)

Java 自带了很多内置注解,

  • @Override: 告诉编译器,这个方法是一个重写方法,如果方法签名错误,编译器会报错。
  • @Deprecated: 标记一个类、方法或字段已过时,不推荐使用。
  • @SuppressWarnings: 告诉编译器忽略某些警告。

自定义注解就是让我们创建属于自己的这种“标签”。


自定义注解的基本语法

创建自定义注解非常简单,使用 @interface 关键字,它看起来很像一个接口。

// 定义一个名为 MyAnnotation 的注解
public @interface MyAnnotation {
    // 注解可以包含成员,它们看起来像抽象方法
    String value();
    int count() default 0; // 可以有默认值
}

语法要点:

java annotation 自定义-图2
(图片来源网络,侵删)
  1. @interface: 使用 @interface 关键字来声明一个注解类型。
  2. 成员: 注解的成员在形式上是抽象方法,成员的返回类型必须是以下几种之一:
    • 基本数据类型 (int, float, boolean, 等)
    • String
    • Class
    • enum
    • Annotation
    • 类型的数组
  3. 默认值: 成员可以有一个默认值,使用 default 关键字指定,如果使用注解时没有为该成员赋值,则会使用默认值。
  4. value 成员: 如果一个成员的名字是 value,那么在使用这个注解时,可以省略成员名直接赋值,这是 Java 注解的一个约定。

使用我们刚刚定义的注解:

@MyAnnotation(value = "Hello Annotation", count = 10)
public class MyClass {
    // ...
}
// 由于 value 是默认成员名,可以省略
@MyAnnotation("Hello Annotation")
public class AnotherClass {
    // ...
}

元注解

元注解是用于注解其他注解的注解,它们用来控制自定义注解的行为,常用的元注解有四个:

  • @Target: 定义注解可以应用在哪些程序元素上(类、方法、字段等)。
  • @Retention: 定义注解的生命周期(源码时、编译时、运行时)。
  • @Documented: 表示这个注解会被包含在 JavaDoc 文档中。
  • @Inherited: 表示允许子类继承父类的注解。

@Target

@Target 的值是 java.lang.annotation.ElementType 枚举中的一个或多个。

  • TYPE: 类、接口、枚举
  • FIELD: 字段(包括枚举常量)
  • METHOD: 方法
  • PARAMETER: 参数
  • CONSTRUCTOR: 构造函数
  • LOCAL_VARIABLE: 局部变量
  • ANNOTATION_TYPE: 注解类型
  • PACKAGE: 包

示例:@Target({ElementType.TYPE, ElementType.METHOD}) 表示这个注解只能用在类/接口和上方法上。

java annotation 自定义-图3
(图片来源网络,侵删)

@Retention

@Retention 的值是 java.lang.annotation.RetentionPolicy 枚举之一。

  • SOURCE: 注解只在源码中存在,编译时被丢弃。@SuppressWarnings
  • CLASS: 注解在 .class 文件中存在,但在运行时被 JVM 丢弃,这是默认策略。
  • RUNTIME: 注解在运行时仍然存在,可以通过反射读取,这是最强大的策略,也是我们通常需要的。

示例:定义一个运行时可见的注解,只能用于方法

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default value";
    int count() default 0;
}

如何使用自定义注解(通过反射)

注解本身不做什么,真正的价值在于在运行时通过反射读取它。java.lang.reflect 包提供了相关的 API。

主要步骤:

  1. 获取 Class 对象。
  2. 使用 getAnnotation()getAnnotations() 方法获取注解。
  3. 判断返回的注解对象是否为 null
  4. 如果不为 null,就可以调用注解的成员方法来获取值。

API 示例:

// 假设我们有一个被注解的类
@MyAnnotation("Test Method", count = 5)
public class TestClass {
    public void test() {
        System.out.println("Test method called.");
    }
}
// 在另一个地方读取这个注解
import java.lang.reflect.Method;
public class AnnotationProcessor {
    public static void main(String[] args) {
        try {
            // 1. 获取 Class 对象
            Class<?> clazz = Class.forName("com.example.TestClass");
            // 2. 获取类上的注解
            MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
            if (classAnnotation != null) {
                System.out.println("Class annotation value: " + classAnnotation.value());
                System.out.println("Class annotation count: " + classAnnotation.count());
            }
            // 3. 获取方法上的注解
            Method method = clazz.getMethod("test");
            MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
            if (methodAnnotation != null) {
                System.out.println("Method annotation value: " + methodAnnotation.value());
                System.out.println("Method annotation count: " + methodAnnotation.count());
            }
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

一个完整的实战案例:一个简单的日志切面

这个例子将展示如何使用自定义注解和反射来模拟一个 AOP 框架的核心功能:在方法执行前后打印日志。

第一步:定义注解

我们创建一个 @LogExecutionTime 注解,用来标记需要记录执行时间的方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 只能用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时可见
public @interface LogExecutionTime {
}

第二步:创建业务服务类

我们创建一个服务类,并在其中的方法上使用我们的自定义注解。

public class UserService {
    @LogExecutionTime
    public void findUserById(long id) {
        System.out.println("正在查找 ID 为 " + id + " 的用户...");
        // 模拟耗时操作
        try {
            Thread.sleep(500); // 暂停 500 毫秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("找到用户: User-" + id);
    }
    public void createUser(String username) {
        System.out.println("正在创建用户: " + username);
        // 没有使用注解,所以不会记录时间
    }
}

第三步:创建注解处理器

这是核心部分,我们将使用反射来检查 UserService 的方法,如果方法上有 @LogExecutionTime 注解,就在其执行前后添加日志逻辑。

import java.lang.reflect.Method;
public class LogExecutionTimeProcessor {
    /**
     * 处理一个对象,扫描其所有方法,并执行带有 @LogExecutionTime 注解的方法
     * @param obj 要处理的对象
     */
    public void process(Object obj) {
        // 1. 获取对象的 Class 对象
        Class<?> clazz = obj.getClass();
        // 2. 获取所有声明的方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            // 3. 检查方法上是否有 @LogExecutionTime 注解
            if (method.isAnnotationPresent
分享:
扫描分享到社交APP
上一篇
下一篇