注解我们经常会用到,或者在jdk源码中也会看到,例如: @Deprecated
以及我们在spring或者springboot中经常用到@Controller、@Service、@Repository、@Entity
等注解。
是否思考过他们是怎么工作的?
下面我们使用 自定义注解 + 反射
给注解加上功能
先贴出整体效果图:
源码:java8环境
一、定义注解
jdk提供了自定义注解的工具类,在 java.lang.annotation
包下
先看下自定义注解的模板:(模板后面有解释)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解
@Target({ElementType.FIELD,ElementType.METHOD}) // 定义 @MyAnnotation注解能加在哪些地方,这里表示可以加在字段和方法上
public @interface MyAnnotation {
String value() default "";
}
第一个注意点是接口需要加上@
,也就是@interface
@Retention 可以用来修饰注解,是注解的注解,称为元注解。有下面三种值
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME
:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;最常用
@Target 表示当前注解可以注解在哪些内容上
ElementType枚举值 | 注解作用的范围 |
---|---|
FIELD | 字段上 |
METHOD | 方法上 |
PARAMETER | 方法的参数上 |
CONSTRUCTOR | 构造方法上 |
PACKAGE | 包上,参考博客 |
MODULE | 模块上 jdk9新增 |
TYPE | 类、接口(包括注解类型)、枚举声明、用户自定义的注解 |
TYPE_USE | 任意使用类型的地方 |
TYPE_PARAMETER | 任何声明类型的地方 |
LOCAL_VARIABLE | 本地变量上 |
ANNOTATION_TYPE | 元注解上 |
RECORD_COMPONENT | record类上jdk14新值 |
二、实体类上使用注解
加上注解的User对象,暂时注解和对象没有任何关系,只是加上了注解,不要妄想这样注解就能用了,功能部分都要我们自己添加进去,不然鬼知道这个注解到底有什么用途。
如果你想让注解注入属性值、判空等操作,那么我们需要通过反射来处理,后面有使用反射给加上注解的字段赋值
public class User {
@MyAnnotation(value = "z3")
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
三、给注解添加功能
通过反射获取字段上的注解,然后获得注解上的值,将注解中的值注入对应字段中
如果要做其它操作,例如判空,赋初值。则得到字段后自己根据需求修改即可。下面只是一个测试案例。
import java.lang.reflect.Field;
import java.util.Optional;
public class MyAnnotationTest {
public static void main(String[] args) {
User user = new User(); // 得到一个一个对象
Field[] fields = user.getClass().getDeclaredFields();// 反射方式得到对象的所有字段
for (int i = 0; i < fields.length; i++) { // 遍历字段数组
fields[i].setAccessible(true); // 将当前字段设置为可访问,不然后面就会报错
MyAnnotation annotation = fields[i].getAnnotation(MyAnnotation.class); // 获得这个字段的注解
Optional<MyAnnotation> annotationOptional = Optional.ofNullable(annotation);// 通过java8的Optional容器包裹
if (!annotationOptional.isEmpty()) { // 判断注解是否存在,age属性没有添加注解因此需要判空
String value = annotation.value(); // 得到注解上的值,如果没有值则会是默认的 "" 注解的value方法的default设置
try {
fields[i].set(user, value); // 给字段赋值
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
fields[i].setAccessible(false); // 重新将字段设置为不可访问
}
System.out.println(user.toString()); // 打印赋值后的user对象
}
}