Java 自定义注解

Java 自定义注解开发

什么是注解

注解就是 Java 提供了一种为程序元素关联任何信息或任何元数据 (metadata)的途径和方法。注解是一个接口,程序可以通过反射来获取指定程序元素的 Annotion 对象,然后通过 Annotation 对象来获取注解里面的元数据

注解位置

Annotation 是在 JDK 1.5 之后被引入的,它可以用来创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation 就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在 Annotation 的 “name = value” 结构中。

注解成员提供的关联信息(参数、注解属性)

Annotation的成员在Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认 语法:允许声明任何Annotation成员的默认值。一个Annotation可以将name=value对作为没有定义默认值的Annotation 成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也 可以被子类覆盖。

注解不会影响程序代码的执行

Annotation能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。另外,尽管一些annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了Annotation,导致了annotation类型在代码中是“不起作用”的; 只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理

注解的作用

注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解析注解 来使用这些数据),常见的作用有以下几种:

  1. 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等;
  2. 在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
  3. 跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;

JDK 自带的注解

@Override

@Override 表示当前方法覆盖了父类的方法

此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息

@Deprecated

@Deprecated 表示方法已经过时,方法上有横线,使用时会有警告。

此注释可用于修辞方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告

@SuppressWarings

@SuppressWarings 表示关闭一些警告信息(通知java编译器忽略特定的编译警告)

用来抑制编译时的警告信息。与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了。如果需要添加多种类型的警告,可以使用 @SuppressWarings({“rawtypes”,“unused”})

image-20210107104703121

public class OverrideDemoImpl implements OverrideDemo {

    // Override 注解会告诉编译器,此方法会执行父类方法覆盖,编译器会按照方法覆盖的要求进行检查
    @Override
    public String overrideDemo() {
        return null;
    }

    // @Deprecated 表示方法已经过时,方法上会出现横线提示
    @Deprecated
    public String deprecatedDemo(){
        return null;
    }

    // @deprecated 标记的方法,使用时会有警告提醒,但不影响正常使用
    public void init(){
        new OverrideDemoImpl().deprecatedDemo();
    }

    // 抑制单类型的警告和抑制所有类型的警告使用方式
    @SuppressWarnings("all")
    public void addItems(String item){
        List items = new ArrayList();
        items.add(item);
    }

    // 抑制多类型的警告
    @SuppressWarnings(value={"unchecked", "rawtypes"})
    public void addItem(String item){
        List items = new ArrayList();
        items.add(item);
    }
}
抑制警告的关键字
关键字用途
all全部类型的警告
boxingto suppress warnings relative to boxing/unboxing operations
castto suppress warnings relative to cast operations
dep-annto suppress warnings relative to deprecated annotation
deprecation使用了某些不赞成使用的类和方法
fallthroughswitch语句执行到底没有break关键字
finally任何finally子句不能正常完成时的警告
hidingto suppress warnings relative to locals that hide variable
incomplete-switchto suppress warnings relative to missing entries in a switch statement (enum case)
nlsto suppress warnings relative to non-nls string literals
nullto suppress warnings relative to null analysis
path在类路径,原文件路径中有不存在的路径
rawtypes没有传递带有泛型的参数
restrictionto suppress warnings relative to usage of discouraged or forbidden references
resource有泛型未指定类型
serial某类实现Serializable 但是没有定义serialVersionUID 这个需要但是不必须的字段
static-accesso suppress warnings relative to incorrect static access
synthetic-accessto suppress warnings relative to unoptimized access from inner classes
unchecked未检查的转化,如集合没有指定类型
unqualified-field-accessto suppress warnings relative to field access unqualified
unused未使用的变量

开发自定义注解

自定义注解的语法规则

  1. 使用@interface关键字定义注解,注意关键字的位置
  2. 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。
  3. 成员以无参数无异常的方式声明,注意区别一般类成员变量的声明
  4. 其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称
  5. 可以使用default为成员指定一个默认值
  6. 成员类型是受限的,合法的类型包括原始类型以及String、Class、Annotation、Enumeration (JAVA的基本数据类型有8种:byte(字节)、short(短整型)、int(整数型)、long(长整型)、float(单精度浮点数类型)、double(双精度浮点数类型)、char(字符类型)、boolean(布尔类型)
  7. 注解类可以没有成员,没有成员的注解称为标识注解,例如JDK注解中的@Override、@Deprecation
  8. 如果注解只有一个成员,并且把成员取名为value(),则在使用时可以忽略成员名和赋值号“=” ,例如JDK注解的@SuppviseWarnings ;如果成员名 不为value,则使用时需指明成员名和赋值号"=",
元注解

就是注解的注解,就是给你自己定义的注解添加注解,你自己定义了一个注解,但你想要你的注解有什么样的功能,此时就需要用元注解对你的注解进行说明了。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。

@Target

@Target说明了Annotation所修饰的对象范围:即注解的作用域,用于说明注解的使用范围(即注解可以用在什么地方,比如类的注解,方法注解,成员变量注解等等)
注意:如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。

取值范围是在java.lang.annotation.ElementType这个枚举中:

Target类型描述
ElementType.TYPE应用于类、接口(包括注解类型)、枚举
ElementType.FIELD应用于属性(包括枚举中的常量)
ElementType.METHOD应用于方法
ElementType.PARAMETER应用于方法的形参
ElementType.CONSTRUCTOR应用于构造函数
ElementType.LOCAL_VARIABLE应用于局部变量
ElementType.ANNOTATION_TYPE应用于注解类型
ElementType.PACKAGE应用于包
ElementType.TYPE_PARAMETER1.8版本新增,应用于类型变量)
ElementType.TYPE_USE1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)
@Retention

@Retention定义了该Annotation被保留的时间长短:

  1. 某些Annotation仅出现在源代码中,而被编译器丢弃;
  2. 而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,
  3. 而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的。
  4. 使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

@Retention的取值范围是在 RetentionPoicy 枚举中

生命周期类型描述
RetentionPolicy.SOURCE编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASSJVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME由JVM 加载,包含在类文件中,在运行时可以被获取到
@Documented

@Documented 用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共API,因此可以被例如 javadoc 此类的工具文档化。Documented 是一个标记注解,没有成员。

@Inherited

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继 承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现, 或者到达类继承结构的顶层。

/**
 * @Target 说明的是当前注解的作用域,即注解的使用范围 1.CONSTRUCTOR:用于描述构造器 2.FIELD:用于描述域 3.LOCAL_VARIABLE:用于描述局部变量
 * 4.METHOD:用于描述方法 5.PACKAGE:用于描述包 6.PARAMETER:用于描述参数 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
 */
@Target({ElementType.METHOD, ElementType.TYPE})
/**
 * @Retention 定义了该注解存在的周期
 * 1.SOURCE:在源文件中有效(即源文件保留)
 * 2.CLASS:在class文件中有效(即class保留)
 * 3.RUNTIME:在运行时有效(即运行时保留)
 */
@Retention(RetentionPolicy.RUNTIME)
/**
 * @Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了 @Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该 class 的子类。
 * 即 @Inherited 修饰的 Annotation 在标注 class 时,其子类相应位置也会自定附带上此 Annotation 注解
 */
@Inherited
/**
 * @Documented 用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共 API ,因此可以被例如 javadoc 此类的工具文档化。
 */
@Documented
public @interface AnnotationDemo {}

注解开发实例

利用反射获取注解信息,结合当前方法参数进行组合操作

Column

@Target(ElementType.FIELD)  // 该注解只作用在属性上
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
public @interface Column {
    // 字段名称
    String columnName();
    // 字段类型
    String dataType();
    // 字段长度
    int dataLength() default 10;
    String constraint() default  "";
}

Table

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String tableName();
}

User

@Table(tableName = "tab_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(columnName = "id", dataType = "int", dataLength = 6, constraint = "primary key")
    private int id;

    @Column(columnName = "name", dataType = "varchar", dataLength = 10, constraint = "unique")
    private String name;

    @Column(columnName = "password", dataType = "varchar", dataLength = 10)
    private String password;

    public User(int id, String name, String password){
        this.id = id;
        this.name = name;
        this.password = password;
    }
}

AutoCreateTable

public class AutoCreateTable {
  public static void main(String[] args) {
      buildSql();
  }

  public static void buildSql() {
    StringBuffer str = new StringBuffer("create table ");

    User user = new User(1, "zhangsan", "123456");

    // 获取 User 类中 Table 注解
     Table table = (Table) user.getClass().getAnnotation(Table.class);

    str.append(table);

    Field[] fields = user.getClass().getDeclaredFields();

    for (Field field : fields) {
      // 获取的属性值包含 serialVersionUID, serialVersionUID.getAnnotation() 为null
      if(!"serialVersionUID".equals(field.getName())){
        Column column = (Column) field.getAnnotation(Column.class);

        String columnName = column.columnName();
        str.append(", columnName : " + columnName);

        String columnType = column.dataType();
        str.append(", columnType : " + columnType);

        int dataLength = column.dataLength();
        str.append(", dataLength : " + dataLength);

        String constraint = column.constraint();
        str.append(", constraint : " + constraint);
      }
    }

    System.out.println(str);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值