廖雪峰Java4反射与泛型-2注解-3处理注解
1.处理注解
- 注解本身对对代码逻辑没有任何影响
- SOURCE类型的注解在编译期就被丢掉了
- CLASS类型的注解仅保存在class文件中
- RUNTIME类型的注解在运行期可以被读取
- 如何使用注解由工具决定
因此如何处理注解只针对RUNTIME类型的注解
如何读取RUNTIME类型的注解
思路:
- Annotation也是class
- 所有Annotation继承自java.lang.annotation.Annotation
- 使用反射API,就可以获取
2.使用反射API读取Annotation
Report.java
package com.reflection;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
Person.java
package com.reflection;
@Report(type=1,level = "error")
public class Person {
}
- 方法1: 判断某个Annotation是否存在,存在再打印注解的信息
- Class.isAnnotationPresent(Class)
- Field.isAnnotationPresent(Class)
- Method.isAnnotationPresent(Class)
- Constructor.isAnnotationPresent(Class)
package com.reflection;
public class Main {
public static void main(String[] args){
Class cls = Person.class;
if (cls.isAnnotationPresent(Report.class)){
Report report = (Report) cls.getAnnotation(Report.class);
int type = report.type();
String level = report.level();
System.out.println(type+"\t"+level);
}
}
}
- 方法2:获取某个Annotation,注解对象不为空,再打印注解的信息
* Class.getAnnotation(Class)
* Field.getAnnotation(Class)
* Method.getAnnotation(Class)
* Constructor.getAnnotation(Class)
* getParameterAnnotations()
package com.reflection;
public class Main {
public static void main(String[] args){
Class cls = Person.class;
Report report = (Report) cls.getAnnotation(Report.class);
if (report != null){
int type = report.type();
String level = report.level();
System.out.println(type+ "\t" + level);
}
}
}
3.读取方法参数的Annotation:
NotNull.java
package com.reflection;
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.PARAMETER)
public @interface NotNull{
}
Range.java
package com.reflection;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Range{
int min() default 1;
int max() default 100;
}
Hello.java
package com.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class Hello {
public String hello(@NotNull String name,@NotNull @Range(max = 5) int age){
return name+"\t"+age;
}
}
TestHello.java
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class TestHello {
public static void main(String[] args) throws Exception{
Class cls = Hello.class;
Method m = cls.getMethod("hello", String.class, int.class);
//方法的参数本身可以看作是一个数组,每一个参数又可以定义多个注解。因此一次获取所有方法的注解,要用2维数组来表示
Annotation[][] annos = m.getParameterAnnotations();
Parameter[] params = m.getParameters();
for(int i=0;i<annos.length;i++){
System.out.print(params[i]+"\t{");
for(Annotation anno:annos[i]){
System.out.print(anno.toString()+"\t");
}
System.out.print("}");
System.out.println();
}
}
}
4.读取字段的Annotation
NotNull.java
package com.reflection;
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)
public @interface NotNull{
}
Range.java
package com.reflection;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Range{
int min() default 1;
int max() default 100;
}
Person.java
package com.reflection;
public class Person{
@NotNull
public String name;
@Range(max=20)
public int age;
public Person(String name,int age){
this.name=name;
this.age = age;
}
}
Main.java
package com.reflection;
//Java注解本身对代码逻辑并不产生任何影响,所以应用的这些注解并不会自动对name和age进行检查,我们需要自己的代码应用这些注解
import java.lang.reflect.Field;
public class Main{
public static void main(String[] args) throws Exception{
Person p1 = new Person("xiaoming",25);
Person p2 = new Person(null,15);
checkPerson(p1);
checkPerson(p2);
}
static void checkPerson(Person p) throws Exception{
System.out.println("check " +p + "...");
Class cls = Person.class;
for(Field f:cls.getFields()){
checkField(f,p);
}
}
static void checkField(Field f,Person p) throws Exception{
if (f.isAnnotationPresent(NotNull.class)){
Object r = f.get(p);
if (r==null){
System.out.println("Error Field " + f.getName() + "is null...");
}
}
if (f.isAnnotationPresent(Range.class)){
Range range = f.getAnnotation(Range.class);
int n = (Integer) f.get(p);//参见反射2field
if(n < range.min() || n > range.max()){
System.out.println("Error Field " + f.getName()+ "is out of range...");
}
}
}
}
5.总结:
- 可以在运行期通过反射读取RUNTIME类型的注解,不要漏写@Retention(RetentionPolicy.RUNTIME)
- 可以通过工具处理注解来实现相应的功能
* 对JavaBean的属性值按规则进行检查
* JUnit会自动运行@Test注解的测试方法
请根据注解:
- @NotNull检查该属性为非null
- @Range检查整形介于minmax,或者检查字符串长度介于minmax
- @ZipCode: 检查字符串是否全部由数字构成,且长度恰好为value
实现对Java Bean的属性值检查。如果检查为通过,抛出异常