一、什么是注解
Annotation(注解)是JDK5.0及以后版本引入的新特性。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。
二、注解能做什么
Annotation提供了一种安全的类似注释的机制,为我们在代码中添加信息提供了一种形式化得方法,使我们可以在稍后某个时刻方便的使用这些数据(通过解析注解来使用这些数据),用来将任何的信息或者元数据与程序元素(类、方法、成员变量等)进行关联。其实就是更加直观更加明了的说明,这些说明信息与程序业务逻辑没有关系,并且是供指定的工具或框架使用的。Annotation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的申明语句中。
Annotation其实是一种接口。通过java的反射机制相关的API来访问Annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。Java语言解释器在工作时会忽略这些Annotation,因此在JVM中这些Annotation是“不起作用”的,只能通过配套的工具才能对这些Annotation类型的信息进行访问和处理。
三、使用注解实现JAVABEAN中属性的自动检测
我们要接下来实现的功能很简单,假设有这么一个场景:外部需要调用某系统接口实现某一功能,其中接口参数是用JavaBean进行传递,JavaBean中定义了几个属性,系统中对这几个属性有着严格的限制,比如:非空、长度限制、参数取值限制等等。
按照传统的方法,我们可能需要针对每个javabean中的每个属性都要写对应的逻辑的判断,设想一下,如果这个系统的接口有1000个,是不是我们应该事先1000个逻辑?NONONO,不需要,java注解功能可以很好的事先这个问题。
1.要实现上面的三个监测,我们需要定义三个不同的注解,代码如下:


1 package com.maomq.testannotation.annotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 /** 10 * 必填项注解 11 * @author maomq 12 * @since 2013-08-29 13 */ 14 @Retention(RetentionPolicy.RUNTIME) 15 @Target({ElementType.FIELD}) 16 @Documented 17 public @interface Required { 18 19 }


1 package com.maomq.testannotation.annotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 /** 10 * 元素长度范围注解 11 * @author maomq 12 * @since 2013-08-29 13 */ 14 @Retention(RetentionPolicy.RUNTIME) 15 @Target({ElementType.FIELD}) 16 @Documented 17 public @interface LengthLimitRange { 18 19 static final int MIN_VALUE = 0; 20 21 static final int MAX_VALUE = 255; 22 23 public int minValue() default MIN_VALUE; 24 25 public int maxValue() default MAX_VALUE; 26 }


1 package com.maomq.testannotation.annotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 /** 10 * 字符串范围注解 11 * @author maomq 12 * @since 2013-08-29 13 */ 14 @Retention(RetentionPolicy.RUNTIME) 15 @Target({ElementType.FIELD}) 16 @Documented 17 public @interface ArrayStringRange { 18 public String[] valueArray(); 19 }
2.注解定义好了,下一步应该定义注解的判断逻辑,这里我们引入一个接口,它将定义所有的监测规则
1 package com.maomq.testannotation.checker; 2 3 import java.lang.reflect.Field; 4 5 /** 6 * 注解检测实现接口类 7 * 8 * @author maomq 9 * @since 2013-08-29 10 */ 11 public interface FieldAnnotationChecker { 12 13 /** 14 * 返回check结果 15 * 16 * @param value 17 * @param field 18 * @return boolean 19 */ 20 public boolean check(Object value, Field field); 21 }


1 package com.maomq.testannotation.checker; 2 3 import java.lang.reflect.Field; 4 import java.util.Collection; 5 import java.util.Map; 6 7 /** 8 * 注解检测实现类 9 * @author maomq 10 * @since 2013-08-29 11 */ 12 public class RequiredAnnotationChecker implements FieldAnnotationChecker { 13 14 @Override 15 public boolean check(Object value, Field field) { 16 if (null == value) 17 { 18 System.out.println("The required value is null!"); 19 return false; 20 } 21 22 if (value instanceof String) 23 { 24 String strValue = (String) value; 25 if(strValue.isEmpty()) 26 { 27 System.out.println("The required value is null!"); 28 return false; 29 } 30 } 31 else if (value instanceof Collection<?>) 32 { 33 Collection<?> collValue = (Collection<?>) value; 34 if(collValue.isEmpty()) 35 { 36 System.out.println("The required value is null!"); 37 return false; 38 } 39 } 40 else if (value instanceof Map<?, ?>) 41 { 42 Map<?, ?> mapValue = ( Map<?, ?>) value; 43 if(mapValue.isEmpty()) 44 { 45 System.out.println("The required value is null!"); 46 return false; 47 } 48 } 49 return true; 50 } 51 52 }


1 package com.maomq.testannotation.checker; 2 3 import java.lang.reflect.Field; 4 import java.util.Collection; 5 import java.util.Map; 6 7 import com.maomq.testannotation.annotation.LengthLimitRange; 8 9 /** 10 * 注解检测实现类 11 * 12 * @author maomq 13 * @since 2013-08-29 14 */ 15 public class LengthLimitRangeAnnotationChecker implements 16 FieldAnnotationChecker { 17 18 @Override 19 public boolean check(Object value, Field field) { 20 if (null == value) { 21 System.out.println("The value is null or empty!"); 22 return true; 23 } 24 25 LengthLimitRange lengthRange = field 26 .getAnnotation(LengthLimitRange.class); 27 28 int maxValue = lengthRange.maxValue(); 29 30 int minValue = lengthRange.minValue(); 31 32 if (value instanceof String) { 33 String strValue = (String) value; 34 if (strValue.length() > maxValue || strValue.length() < minValue) { 35 System.out 36 .println("The input value is out of the value limit range!"); 37 return false; 38 } 39 } else if (value instanceof Collection<?>) { 40 Collection<?> collValue = (Collection<?>) value; 41 if (collValue.size() > maxValue || collValue.size() < minValue) { 42 System.out 43 .println("The input value is out of the value limit range!"); 44 return false; 45 } 46 } else if (value instanceof Map<?, ?>) { 47 Map<?, ?> mapValue = (Map<?, ?>) value; 48 if (mapValue.values().size() > maxValue 49 || mapValue.values().size() < minValue) { 50 System.out 51 .println("The input value is out of the value limit range!"); 52 return false; 53 } 54 } 55 return true; 56 } 57 58 }


1 package com.maomq.testannotation.checker; 2 3 import java.lang.reflect.Field; 4 5 import com.maomq.testannotation.annotation.ArrayStringRange; 6 7 /** 8 * 注解检测实现类 9 * 10 * @author maomq 11 * @since 2013-08-29 12 */ 13 public class ArrayStringRangeAnnotationChecker implements 14 FieldAnnotationChecker { 15 16 @Override 17 public boolean check(Object value, Field field) { 18 if (null == value) { 19 System.out.println("The value is null or empty!"); 20 return true; 21 } 22 23 ArrayStringRange arrayRange = field 24 .getAnnotation(ArrayStringRange.class); 25 26 String[] strArray = arrayRange.valueArray(); 27 28 if (value instanceof String && strArray.length > 0) { 29 for (String strTemp : strArray) { 30 if (strTemp.equalsIgnoreCase(value.toString())) { 31 return true; 32 } 33 } 34 System.out.println("The input value is out of the value range!"); 35 return false; 36 } 37 38 return true; 39 } 40 41 }
3.检测逻辑也完成了,我们是针对每个JAVABEAN中的属性,因此,不可避免的需要一个Util类获取JavaBean中的属性,上代码:
1 package com.maomq.testannotation; 2 3 import java.lang.annotation.Annotation; 4 import java.lang.reflect.Field; 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.List; 8 import java.util.Map; 9 10 import com.maomq.testannotation.annotation.ArrayStringRange; 11 import com.maomq.testannotation.annotation.LengthLimitRange; 12 import com.maomq.testannotation.annotation.Required; 13 import com.maomq.testannotation.checker.ArrayStringRangeAnnotationChecker; 14 import com.maomq.testannotation.checker.FieldAnnotationChecker; 15 import com.maomq.testannotation.checker.LengthLimitRangeAnnotationChecker; 16 import com.maomq.testannotation.checker.RequiredAnnotationChecker; 17 18 /** 19 * 注解检测测试类 20 * @author maomq 21 * @since 2013-08-29 22 */ 23 public class AnnotationCheckerUtil { 24 25 private static Map<Class<?>, FieldAnnotationChecker> holder = new HashMap<Class<?>, FieldAnnotationChecker>(); 26 27 static { 28 holder.put(Required.class, new RequiredAnnotationChecker()); 29 holder.put(LengthLimitRange.class, new LengthLimitRangeAnnotationChecker()); 30 holder.put(ArrayStringRange.class, new ArrayStringRangeAnnotationChecker()); 31 } 32 33 /** 34 * 获取Bean中所有的属性 35 * @param clazz 36 * @param fieldList 37 */ 38 private static void getAllField(Class<?> clazz, List<Field> fieldList) 39 { 40 if (fieldList == null) 41 { 42 fieldList = new ArrayList<Field>(); 43 } 44 45 Field[] fieldArray = clazz.getDeclaredFields(); 46 for (Field fieldTemp : fieldArray) 47 { 48 fieldList.add(fieldTemp); 49 } 50 51 Class<?> superClazz = clazz.getSuperclass(); 52 53 if(superClazz != Object.class) 54 { 55 getAllField(superClazz, fieldList); 56 } 57 } 58 59 /** 60 * 对传入的JAVABean属性进行检测(使用注解) 61 * @param objParam 62 * @return 63 */ 64 public static boolean checkField(Object objParam) 65 { 66 List<Field> fieldList = new ArrayList<Field>(); 67 68 getAllField(objParam.getClass(), fieldList); 69 70 if (fieldList == null || fieldList.isEmpty()) 71 { 72 return true; 73 } 74 75 for (Field fieldTemp : fieldList) 76 { 77 fieldTemp.setAccessible(true); 78 Object value = null; 79 try { 80 value = fieldTemp.get(objParam); 81 } catch (IllegalArgumentException e) { 82 e.printStackTrace(); 83 } catch (IllegalAccessException e) { 84 e.printStackTrace(); 85 } 86 87 Annotation[] fieldAnnotations = fieldTemp.getAnnotations(); 88 89 for(Annotation annotation : fieldAnnotations) 90 { 91 FieldAnnotationChecker checker = holder.get(annotation.annotationType()); 92 93 if (null != checker && !checker.check(value, fieldTemp)) 94 { 95 return false; 96 } 97 } 98 } 99 100 return true; 101 } 102 }
4.上面的工作都已经准备好了,现在可以测试一下了,首先定义一个javabean,并对其中需要检测的项目予以标注。
1 package com.maomq.testannotation; 2 3 import com.maomq.testannotation.annotation.ArrayStringRange; 4 import com.maomq.testannotation.annotation.LengthLimitRange; 5 import com.maomq.testannotation.annotation.Required; 6 7 /** 8 * 注解检测测试Bean类 9 * @author maomq 10 * @since 2013-08-29 11 */ 12 public class TestAnnotationBean { 13 //必填项检测 14 @Required 15 private String userId; 16 //必填,长度限制 17 @Required 18 @LengthLimitRange(minValue = 1, maxValue = 10) 19 private String userName; 20 //必填,取值范围限制 21 @Required 22 @ArrayStringRange(valueArray = {"football", "basketball", "swimming"}) 23 private String habby; 24 25 @SuppressWarnings(value = { "" }) 26 private String description; 27 28 public String getUserId() { 29 return userId; 30 } 31 32 public void setUserId(String userId) { 33 this.userId = userId; 34 } 35 36 public String getUserName() { 37 return userName; 38 } 39 40 public void setUserName(String userName) { 41 this.userName = userName; 42 } 43 44 public String getHabby() { 45 return habby; 46 } 47 48 public void setHabby(String habby) { 49 this.habby = habby; 50 } 51 52 public String getDescription() { 53 return description; 54 } 55 56 public void setDescription(String description) { 57 this.description = description; 58 } 59 }
对上面的结果进行验证:
1 package com.maomq.testannotation; 2 3 /** 4 * 注解检测测试类 5 * @author maomq 6 * @since 2013-08-29 7 */ 8 public class TestAnnotation { 9 10 11 public static void main(String[] args) { 12 TestAnnotationBean testAnnotationBean =new TestAnnotationBean(); 13 14 testAnnotationBean.setUserId("AAA"); 15 testAnnotationBean.setUserName("12345678901"); 16 testAnnotationBean.setHabby("walking"); 17 testAnnotationBean.setDescription("ggsgsgsggs"); 18 19 if(!AnnotationCheckerUtil.checkField(testAnnotationBean)) 20 { 21 System.out.println("Something is wrong!"); 22 } 23 } 24 25 }
输出如我们所料:habby不符合取值范围。
The input value is out of the value limit range!
Something is wrong!
四、小结
通过上面的例子我们可以大致了解注解的功能,如果需要深挖,建议大家学习一下Spring中对java注解功能应用,尤其是涉及事务控制的注解,真的很精髓,有兴趣的同学一定要抽时间学习一下,我这只是抛砖引玉,还望不吝赐教。