自定义注解详解

1 注解的基本概念

1.1 描述

自定义注解是指开发人员可以根据自己的需求定义一些标记,用来标识某些类、方法、变量等,在代码编写和执行的过程中,可以读取这些注解信息,从而实现一些特定的功能。注解是一种元数据,它不直接影响程序代码的执行,但可以为代码提供额外的信息或指示,使代码具有更多的灵活性和可扩展性。在Java中,注解的使用非常广泛,常见的注解包括:@Override、@Deprecated、@SuppressWarnings等。自定义注解的语法格式为:@interface 自定义注解名 {}。需要注意的是,自定义注解也可以定义属性,语法格式为:@interface 自定义注解名 {数据类型 属性名() default 默认值;}。在使用自定义注解时,需要调用注解类的属性方法来获取注解的属性值。

1.2 使用范围

这里是引用注解可以在Java程序中的类、方法、构造函数、成员变量、参数等多个位置使用,并且可以限定使用范围来增强注解的作用力。常见的注解使用范围如下:

  1. @Target注解:该注解用于限定注解的使用范围,可以设置的取值有CONSTRUCTOR、FIELD、LOCAL_VARIABLE、METHOD、PACKAGE、PARAMETER、TYPE等。

  2. @Retention注解:该注解指定了注解的保存方式,即生命周期,可以设置的取值有SOURCE、CLASS、RUNTIME,其中RUNTIME表示注解信息在运行时可以通过反射获取。

  3. @Documented注解:该注解用于指定注解是否包含在Java文档中。

  4. @Inherited注解:该注解表明被注解的类可以被子类继承,这样子类就具有了同样的

2 如何自定义注解

Java语言允许开发者自定义注解,自定义注解的语法格式为:@interface 注解名 { }
其中,@interface表示该语法元素是一个注解声明,注解名就是自定义注解的名称,在{}内部定义的是注解中包含的元素,可以定义多个元素,元素的定义语法格式为: 数据类型 元素名称() default 默认值;
举个例子,假设我们要定义一个注解,用于标记方法的作者和版本号:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodInfo {
    String author() default "Unknown";
    String version() default "1.0";
}

注:@interface 在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。

这样就定义了一个名为MethodInfo的注解,其中包含了两个元素:作者和版本号,并且都有默认值。可以在使用该注解时,给元素传入具体的值,例如:

@MethodInfo(author = "张三", version = "1.2")
public void doSomething() {
    // 方法体
}

通过这种方式,就可以自定义注解并在代码编写和执行过程中使用它们了。

3 常用元注解

@Target

在上述例子中,可以看到注解上使用@Target修饰。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

源码中可以看出,需要配置属性。ElementType:表示自定义注解所能够修饰的对象类型

public enum ElementType {
    /** 类,接口(包括注解类型)或枚举的声明 */
    TYPE,
    /** 属性的声明 */
    FIELD,
    /** 方法的声明 */
    METHOD,
    /** 方法形式参数声明 */
    PARAMETER,
    /** 构造方法的声明 */
    CONSTRUCTOR,
    /** 局部变量声明 */
    LOCAL_VARIABLE,
    /** 注解类型声明 */
    ANNOTATION_TYPE,
    /** 包的声明 */
    PACKAGE,
    /** 类型参数声明(jdk1.8加入) */
    TYPE_PARAMETER,
    /** 任意类型(jdk1.8加入) */
    TYPE_USE
}

@Retention

在上述例子中,可以看到自定义注解上使用@Retention修饰。
@Retention 源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

RetentionPolicy源码:

public enum RetentionPolicy {
    /** 注解将被编译器忽略掉 */
    SOURCE,
    /** 注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为 */
    CLASS,
    /** 注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到 */
    RUNTIME
}

详解:

1、RetentionPolicy.SOURCE:注解将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果。只在java文件中存在。
2、RetentionPolicy.CLASS,注解将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到。
3、RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME。

@Document

@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

@Inherited

@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

4 反射获取注解

public class TestAnnotation {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("com.htf.aggregate.controller.WorkTaskController");
            Method method = clazz.getMethod("createTask", WorkTaskAddUpdateRequest.class);
            if(method.isAnnotationPresent(OperateLog.class)){
                System.out.println("WorkTaskController类上配置了OperateLog注解!");
                //获取该元素上指定类型的注解
                OperateLog operateLog = method.getAnnotation(OperateLog.class);
                System.out.println("operateType: " + operateLog.operateType() +
                        ", module: " + operateLog.module() +
                        ", operationDetail: " + operateLog.operationDetail());
            }else{
                System.out.println("WorkTaskController类上没有配置OperateLog注解!");
            }
        }catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
}

解释:

1、isAnnotationPresent(Class<? extends Annotation> annotationClass)方法是专门判断该元素上是否配置有某个指定的注解;
2、getAnnotation(Class annotationClass)方法是获取该元素上指定的注解。之后再调用该注解的注解类型元素方法就可以获得配置时的值数据;
3、也可以使用 getAnnotations() 获取所有注解。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值