什么是JAVA注解?这是百度百科的解释:
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
②代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
写过JAVA代码的人对注解都不陌生,尤其在各种框架中很常见。这些注解都非常优秀,也很容易理解,但是,为什么要使用注解?注解是如何工作的?让我自定义一个注解来解释,相信理解了这个示例以后。
首先我们分析一个很简单的场景,我们有一个叫Person的POJO类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Person { private String name; private String age; public String getName() { return name; } public void setName(String name) { this .name = name; } public String getAge() { return age; } public void setAge(String age) { this .age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]" ; } }
然后有这样一个HashMap:
1 2 3 4 5 private static Map input = new HashMap(); static { input.put( "name" , "Boaz" ); input.put( "age" , "29" ); } 需求是把这个HashMap的值转换为Person这个pojo类,map中的key值和Person类中的属性名是相同的,我们使用反射的方式来实现,完整代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Main { private static Map input = new HashMap(); static { input.put( "name" , "Boaz" ); input.put( "age" , "29" ); } public static void main(String[] args) { Person person = Map2Pojo(input, Person. class ); System.out.print(person); } private static <T> T Map2Pojo(Map input, Class<T> clazz) { Method[] methods = clazz.getMethods(); T t; try { t = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); return null ; } for (Method method : methods) { input.keySet().forEach((key) -> { if (( "set" + key).equals(method.getName().toLowerCase())) { try { method.invoke(t, input.get(key)); } catch (Exception e) { e.printStackTrace(); return ; } } }); } return t; } } 现在这个 Map2Pojo工具类已经可以工作了,但是现在来了一个新Map 1 2 3 4 5 6 private static Map input = new HashMap(); static { input.put( "Name" , "Boaz" ); input.put( "Age" , "29" ); } 跟之前的Map没有什么不同,只是key值的首字母大写了,显然,上面的 Map2Pojo工具类这个时候就不能工作了。Map2Pojo是一个通用类,可以为 很多Pojo转换服务,显然在这个类里为这个情况添加if-else是不合适的 这个时候就是注解登场了,我们在Person类中增加如下自定义注解: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Person { private String name; private String age; public String getName() { return name; } @MapKey (KeyName = "Name" ) public void setName(String name) { this .name = name; } public String getAge() { return age; } @MapKey (KeyName = "Age" ) public void setAge(String age) { this .age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]" ; } } 跟上面恩Person类相比,在Set方法上增加了注解 @MapKey,这是一个自定义注解,该注解源码如下: 1 2 3 4 5 @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) public @interface MapKey { String KeyName() default "" ; } 定义注解的关键字是 @interface,该注解上还有 @Target (ElementType.METHOD)和 @Retention (RetentionPolicy.RUNTIME)两个注解, 这两个注解被称为"修饰注解的注解",从字面是就可以理解, @Target (ElementType.METHOD) 表示说明该注解只能注解方法, @Retention (RetentionPolicy.RUNTIME) 表示 该注解在运行时有效。 在注解体中,格式就类似于Key-Value格式,KeyName相当于就是键,值则可以在使用注解的时候获取,像Person类中的 @MapKey (KeyName = "Name" ),这里也赋予了一个默认值为空字符串 注解定义和使用后,我们对Map2Pojo工具类稍作修改,修改后完整源码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class Main { private static Map<String, String> input = new HashMap<String, String>(); static { input.put( "Name" , "Boaz" ); input.put( "Age" , "29" ); } public static void main(String[] args) { Person person = Map2Pojo(input, Person. class ); System.out.print(person); } private static <T> T Map2Pojo(Map input, Class<T> clazz) { Method[] methods = clazz.getMethods(); T t; try { t = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); return null ; } for (Method method : methods) { input.keySet().forEach((key) -> { if (method.getAnnotations().length == 1 ) { MapKey mapKey = (MapKey) method.getAnnotations()[ 0 ]; String realKey = mapKey.KeyName(); if (key.equals(realKey)) { try { method.invoke(t, input.get(key)); } catch (Exception e) { e.printStackTrace(); return ; } } } if (( "set" + key).equals(method.getName().toLowerCase())) { try { method.invoke(t, input.get(key)); } catch (Exception e) { e.printStackTrace(); return ; } } }); } return t; } } 修改的部分在22-33行,这里就读取了每个Method的注解MapKey,然后获取注解MapKey中的KeyName值来同Map中的key值进行映射。 这个示例中的注解为运行时注解,大多数自定义注解都是运行时注解。从我的角度来理解,注解可以算做是一种增加重用的机制,在这个示例中,新增了一个注解,Person类就可以在改变注解值的情况下适配不同的Map的Key值!