Java自定义注解的实现

Java自定义注解的实现

简介

注解:说明程序的,给计算机看的。

注释:用文字描述程序的,给程序员看的。

定义:注解(Annotation),也叫元数据,一种代码级别的说明,它是JDK1.5以后版本引入的一个特性,与类、接口、枚举在同一个层次。可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

概念描述:

  • JDK1.5之后的新特性
  • 用于说明程序
  • 使用注解:@注解名称

作用分类:

  • 编写文档:通过代码里标识的注解生成文档
  • 代码分析:通过注解对代码进行分析(利用反射)
  • 编译检查:通过代码里的注解让编译器能够实现基本的编译检查例如Override

JDK中预定义的一些注解

  • @Override:检测被该注解标注的方法是否是继承自夫类/接口的
  • @Deprecated:表示该注解标注的内容,已过时
  • @SuppressWarnings:压制警告

自定义注解

注解的定义规则

观察预定义注解的源码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

可以看出,注解的定义方法是

@元注解
@元注解
....
public @interface 注解名{

}

其中,元注解可以省略,元注解的概念,我们下面再说。

注解的本质

模仿上面定义的方式,创建一个自定义注解

public @interface MyAnno{

}

使用javac命令编译,查看源码:

public interface MyAnno extends java.lang.annotation.Annotation{}

所以注解本质上就是一个接口,其默认继承了Annotation接口

注解中的属性

注解本质就是一个接口,所以属性本质上就是接口中可以定义的成员方法。

接口里面能定义的,注解内都可以定义

属性返回值的要求:

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上的数据类型
  • 不能是void或者包装类型

下面是定义一个包含属性的注解的声明:

package cn.rayfoo.common.annotation.validate;

/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p></p>
 * @date 2020/8/7 11:19
 */
public @interface NotNull {

    int myProp1();

    String myProp2();

    long[] myProp3();

}

此时,在使用注解的时候,必须给属性赋值

属性的修饰default

使用注解时,不需要给注解赋值,可以在属性后加default修饰。不加修饰的为必须指定的内容。

package cn.rayfoo.common.annotation.validate;

/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p></p>
 * @date 2020/8/7 11:19
 */
public @interface NotNull {

    int myProp1() default 10;

    String myProp2() default "rayfoo";

    long[] value();

}

使用带有参数的注解

数字属性使用{}修饰

@NotNull(prop2 = "giao",value={1L,2L})

如果有且只有一个必须赋值的属性,并且属性名为value,属性的name可以省略

@NotNull(value={1L,2L})

数组中如果只有一个元素,大括号可以省略

@NotNull(value=1L})

带有注解类型属性的使用方式

    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "用户名", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "phoneNumber", value = "手机号", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "signature", value = "签名", required = false, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "email", value = "邮箱", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "code", value = "验证码", required = true, dataType = "String", paramType = "query"),
    })

包含枚举属性的注解使用方式

@RequestMapping(value = "/user",method = HttpMethod.DELETE)

元注解

元注解就是用于描述注解的注解

@Target:描述注解能够作用的位置

下面时target的源码,其只有一个枚举类数组类型的属性ElementType[]

@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,

    /**
     * 类型参数上
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

这个可以根据自己的需要,添加合适的修饰。

@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 {
    /**
     * 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
     */
    SOURCE,

    /**
     * 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
     */
    CLASS,

    /**
     * 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;(自定义注解一般都用RUNTIME)
     */
    RUNTIME
}

@Retention:描述注解能够保留的阶段

@Documented:描述注解是否能被抽取到API文档中

加上该注解后,其修饰的注解会被抽取到JavaDoc文档中

@Inherited:描述注解是否可以被子类继承

加入此注解后继承了加了此注解修饰的类的子类,也会自动加上该注解修饰的注解

注解的扫描

加入注解后,对类没有任何的影响,真正进行操作的是读取注解处,我们可以借助反射中的知识,读取并借助注解的属性进行一些操作。一般配合AOP一起使用。

field.isAnnotationPresent(注解.class)
注解 verify = field.getAnnotation(注解.class);
String name = 注解.name();
...判断
原文地址:https://www.cnblogs.com/zhangruifeng/p/13454114.html