通过自定义JAVA注解来理解JAVA注解

来源:互联网 发布:淘宝如何宝贝上架。 编辑:程序博客网 时间:2024/06/01 09:54

什么是JAVA注解?这是百度百科的解释:

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
②代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

①和 ③比较容易理解。重点说说②。写过JAVA代码的人对注解都不陌生,尤其在各种框架中很常见。这些注解都非常优秀,也很容易理解,但是,为什么要使用注解?注解是如何工作的?让我自定义一个注解来解释,相信理解了这个示例以后,对注解的认识就不会模糊了。

首先我们分析一个场景,我们有一个叫Person的POJO类,代码如下:

  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:

private static Map input = new HashMap();static{    input.put("name", "Boaz");    input.put("age", "29");}

需求是把这个HashMap的值转换为Person这个pojo类,map中的key值和Person类中的属性名是相同的,我们使用反射的方式来实现,完整代码如下:

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:

private static Map input = new HashMap();static {    input.put("Name", "Boaz");    input.put("Age", "29");}

跟之前的Map没有什么不同,只是key值的首字母大写了,显然,上面的Map2Pojo工具类这个时候就不能工作了。Map2Pojo是一个通用类,可以为很多Pojo转换服务,在这个类里为这个情况添加if-else来单位为Person这个变化来处理是不合适的。

这个时候就是注解登场了,我们在Person类中增加如下自定义注解:

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,这是一个自定义注解,该注解源码如下:

@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工具类稍作修改,修改后完整源码如下:

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;    }}

修改的部分在21-33行,这里就读取了每个Method的注解MapKey,然后获取注解MapKey中的KeyName值来同Map中的key值进行映射。

这个示例中的注解为运行时注解,大多数自定义注解都是运行时注解。从我的角度来理解,注解可以算做是一种增加重用的机制,在这个示例中,新增了一个注解,Person类就可以在改变注解值的情况下适配不同的Map的Key值!

0 0