Java 注解Annotation总结一

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Weixiaohuai/article/details/85072074

一、注解的概念

注解,英文Annontation,注解就像修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中,起到说明、配置的功能。注解相关的API都在包含在 java.lang.annotation 包中。

 

二、注解的作用

【a】生成文档:如@param/@return等注解。

【b】跟踪代码依赖性,实现替代配置文件功能:可以实现注入配置文件的内容,如@Value("xxx")。

【c】格式检查:如@override 放在方法前,如果方法并不是覆盖了父类方法,那么编译的时候会报错。

 

三、常见的标记类型注解

【a】@Override:使用在方法上面,表示重写父类的方法(即重新实现父类的这个方法)

 @Override
    public String toString() {
        return "";
    }

如果将@Override使用在父类并不存在的方法上面,会报错,如下图

【b】@SuppressWarnings(value = "unchecked") : 用于压制编译时的一些警告信息

【c】@Deprecated:该注解修饰的方法或者类,表示已经过时,不建议我们使用(尽量不要使用,但是还是可以使用的,一般都会有其他新的方法代替过时的方法)

 

四、自定义注解使用的注解(元注解)

【a】@Target : 描述注解使用在什么地方,比如可以使用在类上面、方法上面、字段上面等等。

查看源码可见,@Target注解有一个ElementType[]数组的参数,指定注解使用在什么地方。可选参数如下:

  •   ElementType.CONSTRUCTOR:   用于描述构造器
  •   ElementType.FIELD:   成员变量、对象、属性(包括enum实例)
  •   ElementType.LOCAL_VARIABLE:   用于描述局部变量
  •   ElementType.METHOD:  用于描述方法
  •   ElementType.PACKAGE:  用于描述包
  •   ElementType.PARAMETER:  用于描述参数
  •   ElementType.TYPE:  用于描述类、接口(包括注解类型) 或enum声明

【b】@Retention:表示需要在什么级别保存注解信息,用于描述注解的生命周期

参数是一个RetentionPolicy对象,默认参数为RetentionPolicy.CLASS

可选参数有:

  • RUNTIME : 在运行时保存(可以通过反射机制读取)
  • SOURCE : 源文件中(在编译阶段丢弃)
  • CLASS : 字节码文件中(在类加载的时候丢弃)

【c】@Documented :表示是否将注解信息添加在java文档中

 

五、自定义注释示例一

通过查看上面一些元注解的源码,可以看到如果要自定义注解,需要使用到@Interface注解,该注解用于定义一个自定义注解。

//@Target : 描述注解使用在什么地方
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE})

//@Retention: 表示需要在什么级别保存注解信息,用于描述注解的生命周期
// Runtime : 在运行时保存(可以通过反射机制读取)
// source:源文件中
// class:字节码文件中
@Retention(value = RetentionPolicy.RUNTIME)
//@Documented : 表示是否将注解信息添加在java文档中
@Documented
public @interface MyAnnotation01 {

    //可以通过default指定默认值
    //参数名称: name
    //参数类型: String
//    String name() ;
    String name() default "";

    int num() default 0;

    int index() default -1;

    //使用数组 指定默认值的时候使用{}中括号
    String[] hobby() default {"basketball", "football"};

}

参数解释:

    String name() default "";
    //使用数组 指定默认值的时候使用{}中括号
    String[] hobby() default {"basketball", "football"};

如上:name表示的是参数的名称,String表示的是参数的类型,

hobby表示参数的名称,String[]表示参数的类型,并且可以通过default指定注解参数的默认值,如果是指定默认值为数组的话可以使用中括号{xxx,xxx}指定,如果使用注解的时候指定了值,那么默认值不生效。

下面是自定义注解@MyAnnotation01的使用方法:

//@MyAnnotation01
public class Demo02 extends Object {

    //    @MyAnnotation01
    @MyAnnotation01(name = "zhangsan", num = 25, hobby = {"pingpong"})
    @Override
    public String toString() {
        return "";
    }
    
}

注意:如果自定义注解只有一个属性的话,建议都使用value作为参数的名称

@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAnnotation02 {

    //如果只有一个属性,一般使用value作为属性名(当然也可以使用其他名字)
    String value() default "";

}

 

六、自定义注解示例二

本示例是通过模拟Hibernate持久层的注解@Table,@Column等注解,来理解Hibernate底层是怎么通过使用这些注解就可以反向自动创建数据表的过程。

【a】定义@MyTable注解

@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyTable {
    /**
     * 数据表名称
     */
    String value();
}

【b】定义@MyFileld注解

@Target(ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyField {
    /**
     * 列名称
     */
    String columnName();

    /**
     * 数据类型
     */
    String type();

    /**
     * 长度
     */
    int length();

}

【c】定义实体类Student,并且为实体类加上自定义注解修饰

@MyTable(value = "tb_student")
public class Student {
    @MyField(columnName = "sname", type = "varchar2", length = 128)
    private String name;
    @MyField(columnName = "age", type = "int", length = 3)
    private int age;
    @MyField(columnName = "id", type = "int", length = 128)
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

【d】通过反射获取实体类Student修饰的各个注解,并且拿到注解的属性名称以及属性值,进而构造出创建数据表的sql,就可以实现自动建表。

public class Demo03 {
    public static void main(String[] args) {

        try {
            //通过反射获取Student类对象
            Class clazz = Class.forName("com.wsh.annotation02.Student");

            //获取Student类的所有有效注解
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                //@com.wsh.annotation02.MyTable(value=tb_student)
                System.out.println(annotation);
            }

            //获取类指定的注解
            MyTable myTable = (MyTable) clazz.getAnnotation(MyTable.class);
            System.out.println(myTable.value());  //tb_student

            //获取类的属性的注解
            Field name_field = clazz.getDeclaredField("name");
            MyField nameField = name_field.getAnnotation(MyField.class);
            System.out.println(nameField.columnName());   //sname
            System.out.println(nameField.type());  //varchar2
            System.out.println(nameField.length());  //128

            //至此,各个字段的属性名称、长度、类型都获取了、就可以动态拼接创建数据表的sql...

        } catch (ClassNotFoundException | NoSuchFieldException e) {
            e.printStackTrace();
        }

    }
}

 

七、总结

本文介绍了注解的一些概念、使用自定义注解的一些方法以及示例,在实际工作生活中,自定义注解还是有许多场景可以使用的,比如可以使用自定义注解来实现获取登录用户注入到方法参数里面;使用自定义注解来判断是否需要登录才能操作等等,本文是笔者对注解概念、元注解、自定义注解的一些总结和使用实践,仅供大家学习参考,希望能对大家有所帮助。

猜你喜欢

转载自blog.csdn.net/Weixiaohuai/article/details/85072074