不吹牛逼,撸个注解有什么难的

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

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

01、注解是什么

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

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

public class AutowiredTest {@Autowiredprivate 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;@JsonFieldprivate String bookName;public Writer(int age, String name, String bookName) {this.age = age;this.name = name;this.bookName = bookName;}// getter / setter@Overridepublic 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<String, String> 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<String, String> 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、鸣谢

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

END

IDEA 终于支持中文版和 JDK 直接下载了(太方便了)附新版介绍视频6大分布式定时任务对比除了负载均衡,Nginx 还能干啥?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546241.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DOS命令(bat批处理脚本)遍历目录、遍历子目录下的文件、遍历数字和遍历文件内容

1.遍历目录 格式&#xff1a; for /d %%名称 in (路径) do 具体操作脚本测试&#xff1a;创建test.bat文件&#xff0c;*代表test.bat文件所在的当前目录 echo offfor /d %%a in (*) do (echo %%a)pause2.遍历目录和子目录下的文件 格式&#xff1a; for /r "目录路径&…

几位阿里朋友重写的Java并发编程,牛逼了

昨天在黄金时代群里和读者聊机械键盘大 F 的时候&#xff0c;好朋友 cxuan 推了一篇文章&#xff0c;吸引了我的眼球&#xff0c;名叫“太赞了&#xff0c;阿里几位工程师重写了 《Java 并发编程》”&#xff0c;我看完后&#xff0c;直呼“牛逼了”&#xff0c;就想着赶紧推荐…

FFmpeg 2.1 试用(新版支持HEVC,VP9)

2019独角兽企业重金招聘Python工程师标准>>> 前两天帮一位老师转码图像的时候&#xff0c;无意间发现新版FFmpeg竟然支持了下一代编码标准HEVC&#xff0c;以及Google提出的下一代编码标准VP9。真心没想到FFmpeg对下一代的编码标准支持的是如此之快。我还以为这两种…

Linus shell 在一个脚本中调用另外一个脚本变量

1.新建public.sh文件&#xff0c;并添加以下内容&#xff1a; 2.新建ceshi.sh文件&#xff0c;并添加以下内容&#xff1a; 3.在终端赋予ceshi.sh文件执行权限&#xff0c;并运行该文件。

史上最全的延迟任务实现方式汇总!附代码(强烈推荐)

这篇文章的诞生要感谢一位读者&#xff0c;是他让这篇优秀的文章有了和大家见面的机会&#xff0c;重点是优秀文章&#xff0c;哈哈。 事情的经过是这样的... 不用谢我&#xff0c;送人玫瑰&#xff0c;手有余香。相信接下来的内容一定不会让你失望&#xff0c;因为它将是目前…

面经分享:历时半个月,终于拿到了蚂蚁金服的offer!

在今天&#xff0c;我收到了蚂蚁金服A级的实习录用offer。从开始面试到拿到口头offer&#xff08;四面技术一面HR&#xff09;战线大约拉了半个月&#xff0c;从拿到口头offer到收到正式录用邮件大概又是半个月。思前想后&#xff0c;决定还是做一个整理与总结。一方面是回顾并…

bat批处理脚本获取window系统所有用户名并设置密码,禁用Guest账户

net user可以获取系统用户名&#xff0c;如下&#xff1a; 可以编写代码&#xff0c;bat批处理脚本获取window系统所有用户名并设置密码&#xff0c;如下&#xff0c;如果bat文件有中文&#xff0c;在cmd中执行会出现乱码&#xff0c;解决方法是用记事本打开bat文件&#xff0…

Spring IoC?看这篇文章就够了...

前言刚开始听到 IoC&#xff0c;会觉得特别高大上&#xff0c;但其实明白原理了很简单。跟着我的脚步&#xff0c;一文带你吃透 IoC 原理。本文围绕 是何、为何、如何 来谈&#xff1a;是何上一篇文章有同学问我在官网该看哪些内容&#xff0c;怎么找的&#xff0c;那今天的截图…

交换机arp转ip-mac绑定命令工具-免费版

一、简介 为了防止ARP攻击&#xff0c;我们经常需要在三层交换机上做IP地址与MAC地址的绑定操作。 先要进入System-View系统视图模式&#xff0c;输入"sys"即可。 system-view: [huawei]arp static 192.168.60.58 7813-3a79-d0aa在交换机上使用dis arp命令可以查看…

html 链接 id属性_HTML id属性

html 链接 id属性The id attribute is used to specify a unique id for an element in HTML. This id cannot be used for multiple elements in HTML. You can add the id to any HTML element. id属性用于为HTML中的元素指定唯一的ID 。 该ID不能用于HTML中的多个元素。 您可…

老大说:谁要再用double定义商品金额,就自己收拾东西走

先看现象涉及诸如float或者double这两种浮点型数据的处理时&#xff0c;偶尔总会有一些怪怪的现象&#xff0c;不知道大家注意过没&#xff0c;举几个常见的栗子&#xff1a;典型现象&#xff08;一&#xff09;&#xff1a;条件判断超预期System.out.println( 1f 0.9999999f …

图片一键调整工具V1.0-免费版

一、工具介绍 这是博主自己开发的图片一键调整工具V1.0,它可以调整图片宽度和高度、压缩图片大小、改变图片背景、转换图片格式和图片透明化&#xff0c;都是很常用的功能。操作起来简单方便。 二、工具操作 1.调整图片背景 首先&#xff0c;把该工具软件和图片放到同一文件…

一口气说出 9种 分布式ID生成方式,面试官有点懵了

写在前边前两天公众号有个粉丝给我留言吐槽最近面试&#xff1a;“年前我在公司受点委屈一冲动就裸辞了&#xff0c;然后现在疫情严重两个多月还没找到工作&#xff0c;接了几个视频面试也都没下文。好多面试官问完一个问题&#xff0c;紧接着说还会其他解决方法吗&#xff1f;…

PHP_正则_获取图片所有属性

2019独角兽企业重金招聘Python工程师标准>>> <?php /*PHP正则提取图片img标记中的任意属性*/ $str <center><img src"/uploads/images/20100516000.jpg" height"120" width"120"><br />PHP正则提取或更改图片…

matlab拔河比赛_拔河比赛

matlab拔河比赛Description: 描述&#xff1a; This is a standard interview problem to divide a set of number to two different set where both the subset contains same number of element and have a minimum difference of sum between them using backtracking. 这是…

一款开源免费的SSH/SFTP客户端Electerm,同时支持Linux、MacOS、Windows

简介&#xff1a; Electerm是一个跨平台的Terminal/SSH/SFTP客户端工具&#xff0c;同时支持Linux、MacOS、Windows&#xff0c;基于electron/ssh2/node-pty/xterm/antd/useProxy等开源组件。 下载地址&#xff1a; 官网下载&#xff1a;https://electerm.html5beta.com/ Git…

用了自定义Banner后,SpringBoot瞬间变的高大上了...

Spring Boot 在启动的时候&#xff0c;我们或许想要把自己公司的 logo&#xff0c;或者是项目的 logo 放上去&#xff0c;我们可以试试本文的这些方法&#xff0c;可以让你快速制作一些 Spring Boot 项目启动时的彩蛋&#xff0c;以提高项目的辨识度&#xff0c;或者是纯碎为了…

如何生成高性能的短链接?

前言今天&#xff0c;我们来谈谈如何设计一个高性能短链系统&#xff0c;短链系统设计看起来很简单&#xff0c;但每个点都能展开很多知识点&#xff0c;也是在面试中非常适合考察侯选人的一道设计题&#xff0c;本文将会结合我们生产上稳定运行两年之久的高性能短链系统给大家…

iOS 技术官方 QA

2019独角兽企业重金招聘Python工程师标准>>> Q: 在静态库中使用catagory分类运行时提示"selector not recognized" A: 需要配置下project/target属性 Q: 在iOS7以后怎么截图 A: iOS7 提供了相关API实现截图功能&#xff0c;如:-drawViewHierarchyInRect:a…

IPsec IKEv2(HCIP)

目录 一、IKE介绍 1、IKE介绍 2、IKE的主要作用 3、IKE与IPsec关系 二、IKE基础内容 1、IEK的身份认证方法 数据源认证 预共享密钥PSK 数字证书 数字信封 EAP(IKEv2支持) 数字证书CA如何实现身份认证? 2、IKEv1介绍 IKEv1介绍 IKEv1第一阶段介绍 IKEv1第二阶段…