注解相关概念
什么是注解?
用来说明程序的一个标识,是给计算机看的。注解也叫元数据,是一种代码级别的说明。它是jdk1.5之后引入的一个特性,是一种特殊的接口。可以使用在字段、类、方法、包、参数等上面。
注意:注解本身没有任何功能,仅仅起到一个标示性的作用。我们是通过反射去获取到注解,再根据是否有这个注解、注解中的一些属性去判断执行哪种业务逻辑。
作用分类
- 编写文档
- 通过代码里的注解标识去生成api文档(比如swagger)
- 代码分析
- 通过注解去对代码进行逻辑上的分析(通过反射去操作业务)
- 编译检查
- 通过注解让编译器实现基本的编译检查
- ……
注解举例
- jdk自带的
- @Override
- @Deprecated
- @SuppressWarnings
- ……
- spring的
- @Component
- @Bean
- ……
- ……
自定义注解
格式
public @interface 注解名称 {
// 属性类型 属性名();
// 或
// 属性类型 属性名() default 默认值;
}
本质就是个接口,该接口继承自@Annotation接口
属性
接口中的抽象方法,属性只能是以下几种数据类型:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
如果定义了属性,在使用的时候需要给属性赋值,如果有默认值可以不赋值。
如果只有一个属性需要复制,并且这个属性叫做value,那么就可以省略属性名。
数组赋值时,值用{}包裹,如果数组中只有一个值,那么{}可以省略。
元注解
元注解是用来描述注解的注解。
- @Target:描述该注解的作用范围
- ElementType取值:
- TYPE:作用于类
- METHOD:作用于方法
- FIELD:作用于字段
- ElementType取值:
- @Retention:描述注解被保留的阶段
- RetentionPolicy.RUNTIME:当前描述的注解,会保留到class字节码文件中,并被jvm读取到
- @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否可以被继承
简单案例
定义一个我们自己的注解 @MyAnnocation
MyAnnocation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 说明使用在 类、接口(包括注释类型)、枚举声明 或者 字段上
@Target({ElementType.TYPE, ElementType.FIELD})
// 注释将由编译器记录在类文件中,并在运行时由VM保留,因此可以通过反射方式读取它们
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnocation {
String value();
int num() default 1;
}
为什么value和num都会有括号()呢?
实际上任何注解本身都是接口 ,只不过是隐式继承了Annotation这个接口而已。
Annotation 是jdk1.5 新增的,然而接口定义方法体是jdk1.8才有的特性,而且必须显式定义为default 修饰的普通方法 或者 静态方法。
所以我们定义的注解都算是接口,所以不允许存在方法体。
定义一个测试用到的实体类User,并加上我们自定义的注解
User.java
/**
* @Auth: guohui.wang
* @Date: 2021-03-25 17:42
* @desc:
*/
/*
值为value的字段可以省略 value= ,直接写值。
但是如果设置多个值得时候就需要全都写上了,比如:
@MyAnnocation("用户")
@MyAnnocation(value = "用户")
@MyAnnocation(value = "用户",num = 1)
*/
@MyAnnocation("用户")
public class User {
@MyAnnocation("快乐码农盒")
private String username;
@MyAnnocation
private int age;
private int sex;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
然后创建一个测试类Test进行我们自定义注解的测试
Test.java
import java.lang.reflect.Field;
/**
* @Auth: guohui.wang
* @Date: 2021-03-25 17:43
* @desc:
*/
public class Test {
public static void main(String[] args) throws Exception {
/*
test1()结果:
annotation.value is : 用户
annotation.num is : 1
*/
test1();
/*
test2()结果:
User{username='快乐码农盒', age=1, sex=0}
*/
test2();
}
/**
* 获取类上面标注的注解
*/
public static void test1() {
Class<User> userClass = User.class;
MyAnnocation annotation = userClass.getAnnotation(MyAnnocation.class);
if (annotation != null) {
System.out.println("annotation.value is : " + annotation.value());
System.out.println("annotation.num is : " + annotation.num());
}
}
/**
* 获取字段上面标注的注解
* 并将注解的值赋值给对应字段
*/
public static void test2() throws IllegalAccessException {
User user = new User();
// 获取Class对象
Class<User> userClass = User.class;
// 获取所有字段,括公共,受保护,默认(程序包)访问和私有字段,但不包括继承的字段。
Field[] fields = userClass.getDeclaredFields();
// 对所有字段遍历通过添加的注解对应赋值
for (Field field : fields) {
MyAnnocation annotation = field.getAnnotation(MyAnnocation.class);
if (annotation != null) {
// 设置禁止Java语言访问检查,暴力访问
field.setAccessible(true);
// 对标注注解的字段赋值注解中对应类型的值
if (field.getType() == String.class) {
String value = annotation.value();
field.set(user, value);
} else if (field.getType() == int.class) {
int num = annotation.num();
field.set(user, num);
}
}
}
System.out.println(user);
}
}
执行结果: