Java自定义注解

1. 什么是自定义注解?

通俗解释:

注解就像是给代码贴的“标签”或“便签”。它本身不执行任何操作,但可以为代码提供额外的信息(元数据),告诉编译器、运行时环境或第三方工具如何处理这段代码。

例子:

  • 想象你在一本书上贴了一些标签:
    • 标签A:“这是重点内容,请多看几遍。”
    • 标签B:“这个章节已经过时了,不用看了。”
    • 标签C:“这本书是我借来的,记得还回去。”
  • 这些标签本身不会改变书的内容,但它们可以帮助你更好地理解和使用这本书。

在编程中,注解的作用类似:

  • 它可以标记类、方法、字段等,告诉程序或框架如何处理这些元素。
  • 自定义注解就是你自己定义的“标签”,可以根据需求来设计它的功能。

2. 如何定义自定义注解?

定义一个自定义注解就像定义一个特殊的“模板”,告诉Java这个注解可以包含哪些信息。

语法:

public @interface 注解名称 {
    // 定义属性(可选)
    数据类型 属性名() default 默认值;
}

详细说明:

@interface关键字:

  • 表示这是一个注解类型的定义。
  • 类似于定义一个类,但它是用来描述注解的。

属性定义:

  • 注解可以包含多个属性,每个属性类似于方法声明。
  • 属性可以有默认值,使用default关键字指定。

元注解:

元注解是用来修饰注解的注解,用于配置注解的行为。

1. @Retention
作用:

指定注解的生命周期,即注解在什么时候可用。

取值:
  • RetentionPolicy.SOURCE
    • 注解仅存在于源码中,编译后会被丢弃。
    • 示例:@Override@SuppressWarnings
  • RetentionPolicy.CLASS
    • 注解会保存到编译后的字节码文件中,但在运行时不可用(默认值)。
  • RetentionPolicy.RUNTIME
    • 注解会保存到字节码文件中,并且在运行时可以通过反射访问。
    • 示例:@Deprecated@MyAnnotation(自定义注解,通常用于运行时处理)。
示例:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

2. @Target
作用:

指定注解可以作用的目标类型(如类、方法、字段等)。

取值:
  • ElementType.TYPE:类、接口、枚举等。
  • ElementType.FIELD:字段(包括枚举常量)。
  • ElementType.METHOD:方法。
  • ElementType.PARAMETER:方法参数。
  • ElementType.CONSTRUCTOR:构造器。
  • ElementType.LOCAL_VARIABLE:局部变量。
  • ElementType.ANNOTATION_TYPE:其他注解。
  • ElementType.PACKAGE:包声明。
示例:
@Target(ElementType.METHOD) // 只能作用于方法
public @interface MyAnnotation {
    String value();
}

3. @Documented
作用:

指定注解是否会被包含在JavaDoc中。

特点:
  • 如果注解被@Documented修饰,那么使用该注解的元素会在生成的JavaDoc中显示注解信息。
  • 默认情况下,注解不会出现在JavaDoc中。
示例:
@Documented
public @interface MyAnnotation {
    String value();
}

4. @Inherited
作用:

指定注解是否可以被子类继承。

特点:
  • 如果一个类被@Inherited修饰的注解标记,那么它的子类也会自动继承这个注解。
  • 默认情况下,注解是不可继承的。
示例:
@Inherited
public @interface MyAnnotation {
    String value();
}

@MyAnnotation(value = "父类注解")
class Parent {}

class Child extends Parent {} // 子类会继承@MyAnnotation注解

5. @Repeatable(Java 8引入)
作用:

指定注解是否可以重复应用在同一目标上。

特点:
  • 在Java 8之前,同一个注解不能重复作用于同一个目标。
  • 使用@Repeatable后,注解可以多次标注在同一个目标上。
示例:
// 定义可重复注解的容器
public @interface Schedules {
    Schedule[] value();
}

// 定义可重复注解
@Repeatable(Schedules.class)
public @interface Schedule {
    String day();
}

// 使用可重复注解
@Schedule(day = "Monday")
@Schedule(day = "Friday")
class MyClass {}

总结

以下是Java中常用的元注解及其作用:

元注解作用
@Retention指定注解的生命周期(SOURCE、CLASS、RUNTIME)。
@Target指定注解可以作用的目标(类、方法、字段等)。
@Documented指定注解是否会被包含在JavaDoc中。
@Inherited指定注解是否可以被子类继承。
@Repeatable指定注解是否可以重复应用在同一目标上(Java 8引入)。
完整示例:

假设我们要定义一个注解,用于标记某个方法的重要性,并记录开发者的姓名。

import java.lang.annotation.*;

// 定义一个自定义注解
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.METHOD)         // 注解只能作用于方法
public @interface MyAnnotation {
    String developer() default "mjr"; // 开发者姓名,默认值是"mjr"
    int priority() default 1;         // 方法优先级,默认值是1
}

3. 如何使用自定义注解?

定义好注解后,可以在代码中使用它。就像在书上贴标签一样,你可以将注解标注在类、方法或其他元素上。

示例:

public class TaskManager {

    @MyAnnotation(developer = "张三", priority = 5)
    public void importantTask() {
        System.out.println("这是一个重要任务");
    }

    @MyAnnotation // 使用默认值
    public void normalTask() {
        System.out.println("这是一个普通任务");
    }
}

在这个例子中:

  • importantTask方法被标记为优先级5,开发者是“张三”。
  • normalTask方法使用了默认值(开发者是“Unknown”,优先级是1)。

4. 自定义注解是如何工作的?

自定义注解本身不会执行任何逻辑,但它可以通过反射机制读取并结合业务逻辑来发挥作用。

反射机制:

反射是Java提供的一种动态获取类、方法、字段等信息的能力。通过反射,我们可以读取注解的内容,并根据注解信息执行相应的操作。

示例:

我们可以通过反射读取上面定义的注解,并输出相关信息。

import java.lang.reflect.Method;

public class AnnotationProcessor {
    public static void main(String[] args) throws Exception {
        // 获取目标类的Class对象
        Class<TaskManager> clazz = TaskManager.class;

        // 创建目标类的实例
        TaskManager taskManager = clazz.getDeclaredConstructor().newInstance();

        // 遍历类中的所有方法
        for (Method method : clazz.getDeclaredMethods()) {
            // 检查方法是否被@MyAnnotation注解标记
            if (method.isAnnotationPresent(MyAnnotation.class)) {
                // 获取注解实例
                MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);

                // 输出注解的属性值
                System.out.println("方法名: " + method.getName());
                System.out.println("开发者: " + annotation.developer());
                System.out.println("优先级: " + annotation.priority());

                // 调用方法
                method.invoke(taskManager);
                System.out.println("-------------------------");
            }
        }
    }
}

输出结果:

方法名: importantTask
开发者: 张三
优先级: 5
这是一个重要任务
-------------------------
方法名: normalTask
开发者: Unknown
优先级: 1
这是一个普通任务
-------------------------

5. 自定义注解的应用场景

自定义注解在实际开发中有许多用途,以下是一些常见的应用场景:

(1) 标记代码

  • 用于标记某些类、方法或字段,方便后续处理。
  • 示例:标记需要特殊处理的方法。
    @Deprecated
    public void oldMethod() {}
    

(2) 配置框架

  • 在Spring、Hibernate等框架中,注解用于简化配置。
  • 示例:Spring中的@Controller@Autowired等。

(3) 参数校验

  • 用于验证输入参数的合法性。
  • 示例:JSR-303规范中的@NotNull@Size等。

(4) 日志记录

  • 用于自动记录方法调用的日志。
  • 示例:
    @LogExecutionTime
    public void myMethod() {}
    

(5) 权限控制

  • 用于标记需要权限验证的方法。
  • 示例:
    @RequiresRole("ADMIN")
    public void adminOnlyMethod() {}
    

6. 总结

  • 自定义注解的本质: 是一种轻量级的元数据标记机制,用于为代码添加额外信息。
  • 定义注解的关键点:
    • 使用@interface声明注解。
    • 定义属性(可选)。
    • 使用元注解配置注解行为。
  • 解析注解: 通过反射读取注解信息,结合业务逻辑实现动态功能。
  • 应用场景: 包括标记代码、框架配置、参数校验、日志记录、权限控制等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值