一、什么是注解
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 maomq12 * @since 2013-08-2913 */14 @Retention(RetentionPolicy.RUNTIME)15 @Target({ElementType.FIELD})16 @Documented17 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 maomq12 * @since 2013-08-2913 */14 @Retention(RetentionPolicy.RUNTIME)15 @Target({ElementType.FIELD})16 @Documented17 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 maomq12 * @since 2013-08-2913 */14 @Retention(RetentionPolicy.RUNTIME)15 @Target({ElementType.FIELD})16 @Documented17 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-2910 */11 public interface FieldAnnotationChecker {12 13 /**14 * 返回check结果15 * 16 * @param value17 * @param field18 * @return boolean19 */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 maomq10 * @since 2013-08-2911 */12 public class RequiredAnnotationChecker implements FieldAnnotationChecker {13 14 @Override15 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 maomq13 * @since 2013-08-2914 */15 public class LengthLimitRangeAnnotationChecker implements16 FieldAnnotationChecker {17 18 @Override19 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 = field26 .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.out36 .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.out43 .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() > maxValue49 || mapValue.values().size() < minValue) {50 System.out51 .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 maomq11 * @since 2013-08-2912 */13 public class ArrayStringRangeAnnotationChecker implements14 FieldAnnotationChecker {15 16 @Override17 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 = field24 .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, FieldAnnotationChecker> holder = new HashMap , 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 fieldList) 39 { 40 if (fieldList == null) 41 { 42 fieldList = new ArrayList (); 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 fieldList = new ArrayList (); 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 maomq10 * @since 2013-08-2911 */12 public class TestAnnotationBean {13 //必填项检测14 @Required15 private String userId;16 //必填,长度限制17 @Required18 @LengthLimitRange(minValue = 1, maxValue = 10)19 private String userName;20 //必填,取值范围限制21 @Required22 @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注解功能应用,尤其是涉及事务控制的注解,真的很精髓,有兴趣的同学一定要抽时间学习一下,我这只是抛砖引玉,还望不吝赐教。