注解是在JDK1.5后新增的特性,主要用于标记某个类或属性方法等功能,注解的作用类似于标记标签,使用注解后代表某个类或属性方法是否具有特殊定义(例如:判断此属性的值是否允许为null或值是否合法,以及还可以用于创建对象),也可以当做赋值使用,在获取属性时可以判断是否使用了某些指定的注解
注意:注解只能通过反射机制获取
注解的提取必须使用反射机制,可以在运行期间判断类,属性,方法或其他位置是否使用了指定注解,并且可以获取到注解中存储的数据。
注解提取相关方法
注解的提取方法在Class,Field,Method,Constructor中方法都是相同的,并且使用方式也是相同的,以下方法都是Class类提供的
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
:判断是否使用了指定类型注解annotationClass
:注解类.class类型
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
:通过注解类获取指定注解并返回注解对象annotationClass
:注解类.class类型
public Annotation[] getAnnotations()
:获取使用的所有注解
注意:以上方法仅能在@Retention设置为RUNTIME是才能够获取到
测试
自定义注解类
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
// @Retention(RetentionPolicy.SOURCE)// 在源码期生效,运行期无法获取
// @Retention(RetentionPolicy.CLASS)// 在编译期生效,运行期无法获取
@Retention(RetentionPolicy.RUNTIME)// 在所有位置生效,运行期可以获取到
public @interface Annotation {
// 设置三个属性
int id() default 1;
String name() default "小小";
String value() default "value属性";
}
编写测试类
package annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@annotation.Annotation("value数据更改")
public class Demo {
@annotation.Annotation(name = "name属性数据")
private String value;
@annotation.Annotation(name = "main方法", value = "value属性值")
public static void main(String[] args) throws Exception {
// 获取Demo类的Class类
Class<?> clazz = Class.forName("annotation.Demo");
// 判断类上是否使用了指定注解
boolean isClassAnnotation = clazz.isAnnotationPresent(Annotation.class);
System.out.println("isClassAnnotation = " + isClassAnnotation);
// 获取类上使用的注解
annotation.Annotation clazzAnnotation = clazz.getAnnotation(annotation.Annotation.class);
System.out.println("clazzAnnotation = " + clazzAnnotation);
// 注解中的属性等同于接口中的方法,所以调用方式也是方法
int id = clazzAnnotation.id();
System.out.println("id = " + id);
String name = clazzAnnotation.name();
System.out.println("name = " + name);
String value = clazzAnnotation.value();
System.out.println("value = " + value);
System.out.println("============================================");
// 获取属性的注解
Field field = clazz.getDeclaredField("value");
// 判断是否使用了指定注解
boolean isFieldAnnotation = field.isAnnotationPresent(Annotation.class);
System.out.println("isFieldAnnotation = " + isFieldAnnotation);
// 获取属性上的注解
annotation.Annotation fieldAnnotation = field.getAnnotation(annotation.Annotation.class);
System.out.println("fieldAnnotation = " + fieldAnnotation);
int id1 = fieldAnnotation.id();
System.out.println("id1 = " + id1);
String name1 = fieldAnnotation.name();
System.out.println("name1 = " + name1);
String value1 = fieldAnnotation.value();
System.out.println("value1 = " + value1);
System.out.println("============================================");
// 方法上使用注解
Method main = clazz.getMethod("main", String[].class);
// 判断是否使用了指定注解
boolean isMethodAnnotation = main.isAnnotationPresent(Annotation.class);
System.out.println("isMethodAnnotation = " + isMethodAnnotation);
// 获取注解对象
annotation.Annotation mainAnnotation = main.getAnnotation(annotation.Annotation.class);
System.out.println("mainAnnotation = " + mainAnnotation);
int id2 = mainAnnotation.id();
System.out.println("id2 = " + id2);
String name2 = mainAnnotation.name();
System.out.println("name2 = " + name2);
String value2 = mainAnnotation.value();
System.out.println("value2 = " + value2);
}
}
模拟注解的功能
使用注解校验属性数据是否为null
1、定义异常类
public class FieldValueIllegalException extends RuntimeException {
public FieldValueIllegalException() {
this("字段值非法异常...");
}
public FieldValueIllegalException(String message) {
super(message);
}
}
2、定义注解
/**
* 自定义注解用于检测属性是否为null
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckedFieldIsNull {
}
/**
* 自定义检查年龄注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckedAge {
// 最小年龄
int minAge() default 0;
// 最大年龄
int maxAge() default 160;
}
3、定义实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
@CheckedFieldIsNull// 用于指定此属性不允许为null
private String gender;
@CheckedFieldIsNull// 用于指定此属性不允许为null
private String name;
@CheckedAge(minAge = 18, maxAge = 22)// 检查age是否合法
private Integer age;
}
4、 测试
public class Demo {
public static void main(String[] args) {
Student student = new Student(1, "123", "null", 22);
// 校验方法
checkedObjectField(student);
// 打印数据
System.out.println("student = " + student);
}
/**
* 校验注解方法
*/
private static void checkedObjectField(Object obj) {
// 1. 获取Class类对象
Class<?> clazz = obj.getClass();
// 2. 获取所有的属性
Field[] fields = clazz.getDeclaredFields();
// 3. 循环遍历fields
for (Field field : fields) {
// 4. 设置强制忽略安全检测
field.setAccessible(true);
// 5. 判断属性是否使用了指定注解
if (field.isAnnotationPresent(CheckedFieldIsNull.class)) {
try {
// 6. 如果使用了注解则获取属性的数据
Object value = field.get(obj);
// 7. 判断值是否为null
if (value == null || "".equals(value)) {
// 抛出自定义异常
throw new FieldValueIllegalException("字段<" + field.getName() + ">值为null或空字符串,非法异常!");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 判断是否使用了CheckedAge注解
if (field.isAnnotationPresent(CheckedAge.class)) {
try {
// 获取注解类
CheckedAge annotation = field.getAnnotation(CheckedAge.class);
// 获取最小值
int minAge = annotation.minAge();
// 获取最大值
int maxAge = annotation.maxAge();
// 6. 如果使用了注解则获取属性的数据
Integer value = (Integer) field.get(obj);
// 7. 判断值是否为null
if (value != null && (value < minAge || value > maxAge)) {
// 抛出自定义异常
throw new FieldValueIllegalException("字段<" + field.getName() + ">值超出<" + minAge + "~" + maxAge + ">范围,非法异常!");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
总结:其实就是在运行时通过反射获取某个类、属性、方法让后判断是否用到了某个注解,如果使用了是否含有某注解的行为或赋予属性该注解的行为然后放行,可以在运行时动态的改变类。