杰瑞科技汇

java 定义annotation

注解的基本概念

注解(Annotation)就像一种“标记”,你可以把它放在代码的任何地方(类、方法、字段、参数等),它本身不执行任何操作,但它为其他程序(如编译器、IDE、框架)提供了信息。

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

Java 内置了许多注解,

  • @Override: 告诉编译器这个方法是要重写父类的方法。
  • @Deprecated: 标记某个方法或类已过时,不推荐使用。
  • @SuppressWarnings: 告诉编译器忽略某些警告。

定义一个自定义注解

要定义自己的注解,你需要使用 @interface 关键字,一个注解的定义看起来像一个接口。

基本语法

public @interface MyAnnotation {
    // 在这里定义注解的成员
}

关键组成部分

一个完整的注解定义通常包含以下几个部分:

  1. 注解名称: 使用 @interface 定义。
  2. 成员: 注解可以包含一些成员,它们看起来像抽象方法,成员的类型必须是基本类型、String、Class、枚举、注解或它们的数组。
  3. 默认值: 成员可以有默认值,使用 default 关键字指定。
  4. 元注解: 用于注解其他注解的注解,用来控制注解的行为。

详细步骤与示例

让我们通过一个实际的例子来学习如何定义和使用一个注解。

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

第1步:定义注解本身

假设我们想创建一个用于标记方法作者和创建日期的注解 @Author

Author.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 1. @Target: 指定这个注解可以应用在哪些元素上
// ElementType.METHOD 表示它只能用在方法上
@Target(ElementType.METHOD)
// 2. @Retention: 指定这个注解的保留策略
// RetentionPolicy.RUNTIME 表示注解会在运行时保留,可以通过反射读取
@Retention(RetentionPolicy.RUNTIME)
// 3. 定义注解的成员
// value() 是一个特殊的成员名,使用时可以省略 "value="
public @interface Author {
    String value(); // 成员,类型为 String,没有默认值
    String date() default "2025-01-01"; // 成员,类型为 String,有默认值
}

代码解析:

  • @Target (元注解):

    java 定义annotation-图3
    (图片来源网络,侵删)
    • 作用:限定注解的使用范围。
    • ElementType.METHOD:表示 @Author 只能被用在方法上。
    • 其他可选值:TYPE (类、接口、枚举), FIELD (字段), PARAMETER (参数) 等,可以指定多个,如 @Target({ElementType.METHOD, ElementType.TYPE})
  • @Retention (元注解):

    • 作用:定义注解的生命周期,即注解会被保留到什么时候。
    • RetentionPolicy.SOURCE:只在源码中存在,编译后就被丢弃。
    • RetentionPolicy.CLASS:源码和 .class 文件中都存在,但在运行时被 JVM 丢弃,这是默认策略。
    • RetentionPolicy.RUNTIME:源码、.class 文件和运行时都存在,只有这个级别的注解才能被反射机制读取,如果你需要在运行时处理注解(通过 Spring、MyBatis 等框架),就必须使用这个策略。
  • value()date():

    • 这是 @Author 注解的两个成员。
    • String value(): 这是一个特殊的成员,名为 value,在使用注解时,如果只设置一个值,并且这个值的名称是 value,那么可以省略 value=@Author("张三")
    • String date() default "2025-01-01": 这个成员有一个默认值,如果使用注解时不提供 date 的值,就会自动使用这个默认值。

第2步:使用自定义注解

现在我们可以在代码中使用刚刚定义的 @Author 注解了。

UserService.java

public class UserService {
    @Author(value = "李四") // date 会使用默认值 "2025-01-01"
    public void createUser(String username) {
        System.out.println("正在创建用户: " + username);
    }
    @Author(value = "王五", date = "2025-10-27") // 同时指定 value 和 date
    public void deleteUser(String username) {
        System.out.println("正在删除用户: " + username);
    }
    // 由于 value 是特殊成员,可以省略 value=
    @Author("赵六")
    public void updateUser(String username, String newInfo) {
        System.out.println("正在更新用户: " + username);
    }
}

第3步:通过反射处理注解(读取注解信息)

注解本身没有作用,关键在于如何读取它,我们通过 Java 的反射 API 来读取 @Author 注解的信息。

AnnotationProcessor.java

import java.lang.reflect.Method;
public class AnnotationProcessor {
    public static void main(String[] args) {
        // 1. 获取 UserService 类的 Class 对象
        Class<?> userServiceClass = UserService.class;
        // 2. 获取所有声明的方法
        Method[] methods = userServiceClass.getDeclaredMethods();
        System.out.println("------ 正在处理 UserService 中的方法 ------");
        // 3. 遍历每个方法
        for (Method method : methods) {
            // 4. 检查方法上是否有 @Author 注解
            if (method.isAnnotationPresent(Author.class)) {
                // 5. 如果存在,获取该注解的实例
                Author authorAnnotation = method.getAnnotation(Author.class);
                // 6. 从注解实例中获取成员的值
                String authorName = authorAnnotation.value();
                String creationDate = authorAnnotation.date();
                // 7. 打印信息
                System.out.println("方法名: " + method.getName());
                System.out.println("  - 作者: " + authorName);
                System.out.println("  - 创建日期: " + creationDate);
                System.out.println("-------------------------------------");
            }
        }
    }
}

运行结果:

------ 正在处理 UserService 中的方法 ------
方法名: createUser
  - 作者: 李四
  - 创建日期: 2025-01-01
-------------------------------------
方法名: deleteUser
  - 作者: 王五
  - 创建日期: 2025-10-27
-------------------------------------
方法名: updateUser
  - 作者: 赵六
  - 创建日期: 2025-01-01
-------------------------------------

高级特性:注解继承

有时候我们希望一个类上的注解能被它的所有方法继承,Java 8 引入了 @Inherited 元注解来实现这一点。

InheritedAnnotation.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // 只能用在类上
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 表示这个注解可以被继承
public @interface InheritedAnnotation {
    String value();
}

BaseService.java

@InheritedAnnotation(value = "基础服务")
public class BaseService {
    public void commonMethod() {
        System.out.println("这是一个通用方法");
    }
}

DerivedService.java

// 继承自 BaseService
public class DerivedService extends BaseService {
    @Override
    public void commonMethod() {
        System.out.println("这是 DerivedService 的方法");
    }
}

测试继承性:

// 在另一个类中测试
InheritedAnnotation annotation = DerivedService.class.getAnnotation(InheritedAnnotation.class);
System.out.println(DerivedService.class.getSimpleName() + " 上的注解值: " + annotation.value());
// 输出: DerivedService 上的注解值: 基础服务

注意:@Inherited 只对类有效,对接口无效,并且子类不会继承父类方法的注解,只会继承父类类本身的注解。

  1. 定义注解:使用 @interface 关键字。
  2. 添加成员:在注解内部定义抽象方法来表示成员。
  3. 提供默认值:使用 default 关键字为成员设置默认值。
  4. 使用元注解
    • @Target:控制注解的使用位置。
    • @Retention:控制注解的生命周期(RUNTIME 是反射的关键)。
    • @Documented:表示注解会包含在 Javadoc 中。
    • @Inherited:表示注解可以被继承。
  5. 使用注解:像 @Override 一样,将注解放在代码元素前。
  6. 处理注解:通过 Java 反射 API(Class.getAnnotation(), Method.isAnnotationPresent() 等)在运行时读取注解信息,并执行相应的逻辑。

掌握自定义注解是理解现代 Java 框架(如 Spring, Hibernate, JUnit)工作原理的基础,因为它正是这些框架实现“零配置”和“声明式编程”的核心技术。

分享:
扫描分享到社交APP
上一篇
下一篇