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