文章来源:https://mp.weixin.qq.com/s/z8qA8i3F8-SDbcTDMdJgjw

作者:沉默王二

注解是 Java 中非常重要的一部分,但经常被忽视也是真的。之所以这么说是因为我们更倾向成为一名注解的使用者而不是创建者。@Override 注解用过吧?@Service注解用过吧?但你知道怎么自定义一个注解吗?

恐怕你会摇摇头,摆摆手,不好意思地承认自己的确没有自定义过。

01、注解是什么

注解(Annotation)是在 Java 1.5 时引入的概念,同 class 和 interface 一样,也属于一种类型。注解提供了一系列数据用来装饰程序代码(类、方法、字段等),但是注解并不是所装饰代码的一部分,它对代码的运行效果没有直接影响(这句话怎么理解呢?),由编译器决定该执行哪些操作。

来看一段代码,我随便写的,除了打印到控制台的那句宣传语,其他都不重要,嘻嘻。

public class AutowiredTest {    @Autowired    private String name;    public static void main(String[] args) {        System.out.println("沉默王二,一枚有趣的程序员");    }}

注意到 @Autowired 这个注解了吧?它本来是为 Spring 容器注入 Bean 的,现在被我无情地扔在了成员变量 name 的身上,但这段代码所在的项目中并没有启用 Spring,意味着 @Autowired 注解此时只是一个摆设。

我之所以举这个无聊的例子就是为了证明一个观点:注解对代码的运行效果没有直接影响,明白我的用意了吧?

02、注解的生命周期

注解的生命周期有 3 种策略,定义在 RetentionPolicy 枚举中。

1)SOURCE:在源文件中有效,被编译器丢弃。

2)CLASS:在编译器生成的字节码文件中有效,但在运行时会被处理类文件的 JVM 丢弃。

3)RUNTIME:在运行时有效。这也是注解生命周期中最常用的一种策略,它允许程序通过反射的方式访问注解,并根据注解的定义执行相应的代码。

03、注解装饰的目标

注解的目标定义了注解将适用于哪一种级别的 Java 代码上,有些注解只适用于方法,有些只适用于成员变量,有些只适用于类,有些则都适用。

截止到 Java 9,注解的类型一共有 11 种,定义在 ElementType 枚举中。

1)TYPE:用于类、接口、注解、枚举

2)FIELD:用于字段(类的成员变量),或者枚举常量

3)METHOD:用于方法

4)PARAMETER:用于普通方法或者构造方法的参数

5)CONSTRUCTOR:用于构造方法

6)LOCAL_VARIABLE:用于变量

7)ANNOTATION_TYPE:用于注解

8)PACKAGE:用于包

9)TYPE_PARAMETER:用于泛型参数

10)TYPE_USE:用于声明语句、泛型或者强制转换语句中的类型

11)MODULE:用于模块

04、开始撸注解

说再多,都不如撸个注解来得让人心动。撸个什么样的注解呢?一个字段注解吧,它用来标记对象在序列化成 JSON 的时候要不要包含这个字段。

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface JsonField {    public String value() default "";}

1)JsonField 注解的生命周期是 RUNTIME,也就是运行时有效。

2)JsonField 注解装饰的目标是 FIELD,也就是针对字段的。

3)创建注解需要用到 @interface 关键字。

4)JsonField 注解有一个参数,名字为 value,类型为 String,默认值为一个空字符串。

为什么参数名要为 value 呢?有什么特殊的含义吗?

当然是有的,value 允许注解的使用者提供一个无需指定名字的参数。举个例子,我们可以在一个字段上使用 @JsonField(value = "沉默王二"),也可以把 value = 省略,变成 @JsonField("沉默王二")。

那 default "" 有什么特殊含义吗?

当然也是有的,它允许我们在一个字段上直接使用 @JsonField,而无需指定参数的名和值。

05、使用注解

是骡子是马拉出来遛遛,对吧?现在 @JsonField 注解已经撸好了,接下来就到了怎么使用它的环节。

假设有一个作者类,他有 3 个字段,分别是 age、name 和 bookName,后 2 个是必须序列化的字段。

public class Writer {    private int age;    @JsonField("writerName")    private String name;    @JsonField    private String bookName;    public Writer(int age, String name, String bookName) {        this.age = age;        this.name = name;        this.bookName = bookName;    }    // getter / setter    @Override    public String toString() {        return "Writer{" +                "age=" + age +                ", name='" + name + ''' +                ", bookName='" + bookName + ''' +                '}';    }}

1)name 上的 @JsonField 注解提供了显式的字符串值。

2)bookName 上的 @JsonField 注解使用了缺省项。

接下来,我们来编写序列化类 JsonSerializer,内容如下:

public class JsonSerializer {    public static String serialize(Object object) throws IllegalAccessException {        Class> objectClass = object.getClass();        Map jsonElements = new HashMap<>();        for (Field field : objectClass.getDeclaredFields()) {            field.setAccessible(true);            if (field.isAnnotationPresent(JsonField.class)) {                jsonElements.put(getSerializedKey(field), (String) field.get(object));            }        }        return toJsonString(jsonElements);    }    private static String getSerializedKey(Field field) {        String annotationValue = field.getAnnotation(JsonField.class).value();        if (annotationValue.isEmpty()) {            return field.getName();        } else {            return annotationValue;        }    }    private static String toJsonString(Map jsonMap) {        String elementsString = jsonMap.entrySet()                .stream()                .map(entry -> """ + entry.getKey() + "":"" + entry.getValue() + """)                .collect(Collectors.joining(","));        return "{" + elementsString + "}";    }}

JsonSerializer 类的内容看起来似乎有点多,但不要怕,我一点点来解释,直到你搞明白为止。

1)serialize() 方法是用来序列化对象的,它接收一个 Object 类型的参数。objectClass.getDeclaredFields() 通过反射的方式获取对象声明的所有字段,然后进行 for 循环遍历。在 for 循环中,先通过 field.setAccessible(true) 将反射对象的可访问性设置为 true,供序列化使用(如果没有这个步骤的话,private 字段是无法获取的,会抛出 IllegalAccessException 异常);再通过 isAnnotationPresent() 判断字段是否装饰了 JsonField 注解,如果是的话,调用 getSerializedKey() 方法,以及获取该对象上由此字段表示的值,并放入 jsonElements 中。

2)getSerializedKey() 方法用来获取字段上注解的值,如果注解的值是空的,则返回字段名。

3)toJsonString() 方法借助 Stream 流的方式返回格式化后的 JSON 字符串。如果对 Stream 流比较陌生的话,请查阅我之前写的 Stream 流入门。

看完我的解释,是不是豁然开朗了?

接下来,我们来写一个测试类 JsonFieldTest,内容如下:

public class JsonFieldTest {    public static void main(String[] args) throws IllegalAccessException {        Writer cmower = new Writer(18,"沉默王二","Web全栈开发进阶之路");        System.out.println(JsonSerializer.serialize(cmower));    }}

程序输出结果如下:

{"bookName":"Web全栈开发进阶之路","writerName":"沉默王二"}

从结果上来看:

1)Writer 类的 age 字段没有装饰 @JsonField 注解,所以没有序列化。

2)Writer 类的 name 字段装饰了 @JsonField 注解,并且显示指定了字符串“writerName”,所以序列化后变成了 writerName。

3)Writer 类的 bookName 字段装饰了 @JsonField 注解,但没有显式指定值,所以序列化后仍然是 bookName。

06、鸣谢

好了,我亲爱的读者朋友,以上就是本文的全部内容了,是不是感觉撸个注解也没什么难的?你也赶紧动动小手试试吧!

对了,在这里说一下,我目前是在职Java开发,如果你现在正在学习Java,了解Java,渴望成为一名合格的Java开发工程师,在入门学习Java的过程当中缺乏基础入门的视频教程,可以关注并私信我:01。获取。我这里有最新的Java基础全套视频教程。

@jsonfield注解_好了,不装了,撸个注解有什么难的!相关推荐

  1. java spring框架 注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

  2. @async注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

  3. @autowired注解_品Spring:对@Autowired和@Value注解的处理方法(文末附spring系列资源合集)...

    作者:编程新说李新杰 出自:微信公众号"编程新说" 原文:品Spring:对@Autowired和@Value注解的处理方法 在Spring中能够完成依赖注入的注解有JavaSE提 ...

  4. @order注解_别再用ifelse了,用注解去代替他吧

    来自公众号:咖啡拿铁 策略模式 经常在网上看到一些名为"别再if-else走天下了","教你干掉if-else"等之类的文章,大部分都会讲到用策略模式去代替if ...

  5. java spring 条件注解_【Spring】Spring高级话题-条件注解-@Condition

    进行本示例的演示,需要先配置好Maven和Spring哦. 见: [Spring]基于IntelliJ IDEA搭建Maven 分析 通过profile,我们可以获得不同的profile,我们可以获得 ...

  6. java编译时注解_简单介绍 Java 中的编译时注解

    1. 前言 上一篇 主要介绍了什么是 注解 (Annotation) 以及如何读取 运行时注解 中的数据, 同时用注解实现了简单的 ORM 功能. 这次介绍另一部分: 如何读取 编译时注解 ( Ret ...

  7. boot spring 跨域注解_每日一问学技术:@SpringBootApplication注解干了个啥?

    @SpringBootApplication注解干了个啥? 通常使用Spring Boot的应用程序的主类是这样的: import org.springframework.boot.SpringApp ...

  8. java为什么引入注解_说说Spring中为何要引入Lookup注解

    前言 我们先探一探官方文档关于Method Injection的章节是怎么说的: In most application scenarios, most beans in the container ...

  9. 撸个注解有什么难的 请看这里

    01.注解是什么 注解(Annotation)是在 Java 1.5 时引入的概念,同 class 和 interface 一样,也属于一种类型.注解提供了一系列数据用来装饰程序代码(类.方法.字段等 ...

最新文章

  1. Guava Cache本地缓存在 Spring Boot应用中的实践
  2. 用python画烟花-python 实现漂亮的烟花,樱花,玫瑰花
  3. 对物联网操作系统特征和定位的思考
  4. uCOS中任务调度时的上下文切换
  5. c语言补全 subilme_Sublime Text3 C语言插件
  6. 这是你从未见过的组件库 -- Android 上的手绘风格组件
  7. GPG96244QS1屏驱动难题
  8. python-函数与变量的定义-标识符的命名规范
  9. [ES6] 细化ES6之 -- 对象的扩展
  10. map to javaBean
  11. 《SpringBoot实战》笔记1
  12. 山东工商学院第五届ACM趣味编程大赛感想
  13. 多业务建模在美团搜索排序中的实践
  14. Bootstrap关于导航条点击后移出此区域背景颜色变白
  15. 【脚本语言系列】关于Python测试工具lettuce
  16. 这18个网站能让你的页面背景炫酷起来
  17. AMD首款5纳米PC处理器锐龙7000亮相,频率首破5GHz大关,单核性能提升15%
  18. [原]删除dboy病毒
  19. 如何检查后台服务(Android的Service类)是否正在运行?
  20. __str__()方法

热门文章

  1. QFileDialog::getOpenFileName
  2. C/C++中extern关键字详解与应用
  3. 浅析ASP.NET的Page.IsPostBack 属性
  4. python-matplotlib
  5. 调用max函数求两个数中较大值
  6. android 始终显示overflow菜单
  7. 动静分离和前后端分离
  8. cast函数 oracle 日期_从Oracle到PG 该做的改造工作一个都不能少!
  9. 区块链去中心化有那么有用吗?
  10. SeqGAN——对抗思想与增强学习的碰撞