Jackson 2.x 系列【23】注解内省 AnnotationIntrospector

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

文章目录

    • 1. 前言
    • 2. AnnotationIntrospector
    • 3. JacksonAnnotationIntrospector
    • 4. AnnotationIntrospectorPair
    • 5. NopAnnotationIntrospector
    • 6. 案例演示
      • 6.1 关闭注解功能
      • 6.2 覆盖默认
      • 6.3 自定义注解

1. 前言

Annotation注解是JDK 5引入的一种机制,本身只是起到了标注作用,需要使用注解处理器、反射机制在运行时添加对应注解的处理逻辑。 例如Spring中包含了大量的注解,在运行时,Spring容器通过的反射机制获取类的注解信息,并根据这些信息创建和配置Bean对象。

Jackson中也包含了很多注解,用于配置序列化/反序列化行为,那么这些注解是如何生效的呢?

2. AnnotationIntrospector

AnnotationIntrospector注解内省抽象类,由jackson-databind模块提供,定义了一系列基于注解的内省API,用于检查类的注解并据此进行配置。

继承结构如下:
在这里插入图片描述

Introspector翻译过来是内省的意思,在计算机领域,内省指的是计算机程序在运行时检查对象类型的能力,通常也可以称作运行时类型检查,是一种检查对象以了解其属性、方法和事件等信息的机制。

AnnotationIntrospector定义了很多抽象方法,常用的有:

  • findDeserializer(Annotated a): 查找与注解关联的反序列化器
  • findSerializer(Annotated a):查找与注解关联的序列化器
  • findNullSerializer(Annotated a):查找处理null值的序列化器
  • hasIgnoreMarker(AnnotatedMember m):检查一个成员(字段或方法)是否标记为忽略
  • findFormat(Annotated memberOrClass):查询JsonFormat注解

例如,当前对象属性包含了一个@JsonProperty注解,username会被序列化为name字段:

    @JsonProperty("name")String username;

在序列化的过程中,需要获取对象的属性名称,AnnotationIntrospector中声明了相关方法:

	// 序列化是查找属性名称public PropertyName findNameForSerialization(Annotated a) {return null;}

在执行过程中,调用到实现类JacksonAnnotationIntrospector的实现方法,会检查是否包含@JsonGetter@JsonProperty注解,优先使用注解上的配置作为属性名称:

    public PropertyName findNameForSerialization(Annotated a) {boolean useDefault = false;// 查询 @JsonGetterJsonGetter jg = (JsonGetter)this._findAnnotation(a, JsonGetter.class);if (jg != null) {String s = jg.value();if (!s.isEmpty()) {return PropertyName.construct(s);}useDefault = true;}// 查询 @JsonProperty JsonProperty pann = (JsonProperty)this._findAnnotation(a, JsonProperty.class);if (pann != null) {// 存在注解时String ns = pann.namespace();if (ns != null && ns.isEmpty()) {ns = null;}// return PropertyName.construct(pann.value(), ns);} else {return !useDefault && !this._hasOneOf(a, ANNOTATIONS_TO_INFER_SER) ? null : PropertyName.USE_DEFAULT;}}

3. JacksonAnnotationIntrospector

JacksonAnnotationIntrospectorAnnotationIntrospector的默认标准实现,用于分析处理Jackson中提供的标准注解,ObjectMapper中可以看到初始化逻辑:

public class ObjectMapper extends ObjectCodec implements Versioned, Serializable {protected static final AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR =new JacksonAnnotationIntrospector();
}

4. AnnotationIntrospectorPair

AnnotationIntrospectorPair是一个Helper类,内部维护了两个内省器,一个首要一个辅助。

public class AnnotationIntrospectorPair extends AnnotationIntrospector implements Serializable {protected final AnnotationIntrospector _primary;protected final AnnotationIntrospector _secondary;//。。。。。。。。。。
}

例如,其查询注解配置的序列化器方法中,首先在首选的内省器中查询,没有查询到则在辅助中再次查询:

    public Object findSerializer(Annotated am) {Object r = this._primary.findSerializer(am);return this._isExplicitClassOrOb(r, JsonSerializer.None.class) ? r : this._explicitClassOrOb(this._secondary.findSerializer(am), JsonSerializer.None.class);}

当处理自定义注解时,可以设置JacksonAnnotationIntrospector为首选,自定义的AnnotationIntrospector为辅助内省器。

5. NopAnnotationIntrospector

NopAnnotationIntrospector即没有任何操作的内省抽象类,没有实现父类的任何方法,主要用于:

  • 在不需要处理注解的场景,可以用它覆盖JacksonAnnotationIntrospector
  • 构建自定义的AnnotationIntrospector实现
public abstract class NopAnnotationIntrospectorextends AnnotationIntrospectorimplements java.io.Serializable {private static final long serialVersionUID = 1L;// 内部实例public final static NopAnnotationIntrospector instance = new NopAnnotationIntrospector() {private static final long serialVersionUID = 1L;@Overridepublic Version version() {return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;}};@Overridepublic Version version() {return Version.unknownVersion();}
}

6. 案例演示

6.1 关闭注解功能

在不需要处理注解的场景,可以关闭注解内省功能,提升性能(极少场景)。

例如,下面的POJO类:

public class IntrospectorVO {@JsonIgnorepublic String name;public Long id;public IntrospectorVO(String name, Long id) {this.name = name;this.id = id;}
}

序列化是name字段将被忽略:

{"id":1767798780627279333}

使用NopAnnotationIntrospector替换默认的JacksonAnnotationIntrospector,所有的注解将不生效:

        IntrospectorVO vo=new IntrospectorVO("阿坤",1767798780627279333L);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setAnnotationIntrospector(NopAnnotationIntrospector.nopInstance());String jsonStr = objectMapper.writeValueAsString(vo);System.out.println(jsonStr);

6.2 覆盖默认

可以继承JacksonAnnotationIntrospector,重写方法修改默认处理逻辑。

例如重写判断属性是否被忽略的_isIgnorable方法,返回false,那么所有的属性都不会被忽略:

public class CustomJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {@Overrideprotected boolean _isIgnorable(Annotated a) {System.out.println("_isIgnorable");return false;}
}

测试代码:

        IntrospectorVO vo=new IntrospectorVO("阿坤",1767798780627279333L);ObjectMapper objectMapper = new ObjectMapper();// objectMapper.setAnnotationIntrospector(NopAnnotationIntrospector.nopInstance()); // 关闭注解objectMapper.setAnnotationIntrospector(new CustomJacksonAnnotationIntrospector());String jsonStr = objectMapper.writeValueAsString(vo);System.out.println(jsonStr);

6.3 自定义注解

在Jackson 2.x 系列【22】自定义序列化/反序列化器中,有演示过使用自定义注解指定序列化器:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = ListToStringJsonSerializer.class)
@JsonDeserialize(using = ListToStringJsonDeserializer.class)
public @interface JsonListToCommaSplitString {}

除此之外,还可以通过自定义AnnotationIntrospector重写查询序列化/反序列化器方法。

例如上述注解改为:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
public @interface JsonListToCommaSplitString {}

自定义AnnotationIntrospector

public class MyAnnotationIntrospector extends NopAnnotationIntrospector {private static final long serialVersionUID = 1L;public static final MyAnnotationIntrospector instance = new MyAnnotationIntrospector();@Overridepublic Object findSerializer(Annotated a) {JsonListToCommaSplitString ann = (JsonListToCommaSplitString) this._findAnnotation(a, JsonListToCommaSplitString.class);if (ann != null) {return ListToStringJsonSerializer.instance;}return null;}@Overridepublic Object findDeserializer(Annotated a) {JsonListToCommaSplitString ann = (JsonListToCommaSplitString) this._findAnnotation(a, JsonListToCommaSplitString.class);if (ann != null) {return ListToStringJsonDeserializer.instance;}return null;}
}

配置:

        IntrospectorVO vo = new IntrospectorVO("阿坤", 1767798780627279333L);ObjectMapper objectMapper = new ObjectMapper();// objectMapper.setAnnotationIntrospector(NopAnnotationIntrospector.nopInstance()); // 关闭注解//objectMapper.setAnnotationIntrospector(new CustomJacksonAnnotationIntrospector());objectMapper.setAnnotationIntrospector(AnnotationIntrospector.pair(objectMapper.getSerializationConfig().getAnnotationIntrospector(), // jackson 原有的MyAnnotationIntrospector.instance // 自定义的作为辅助方案));String jsonStr = objectMapper.writeValueAsString(vo);System.out.println(jsonStr);

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

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

相关文章

基于springboot实现医疗病历互换系统项目【项目源码+论文说明】

基于springboot实现医疗病历交互系统演示 摘要 进入21世纪,计算机技术迅速向着网络化的、集成化方向发展。传统的单机版应用软件正在逐渐退出舞台,取而代之的是支持网络、支持多种数据信息的新一代网络版应用软件,形成了信息化的社会。信息…

【canvas】canvas基础使用(七):绘制图像

简言 学习canvas如何绘制图片或视频。 绘制图像 给定一个图像,一般使用drawImage()方法绘制。 drawImage 绘制图像 Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种在画布(Canvas)上绘制图像的方式。 语法&…

C语言单链表详解

链表和顺序表的区别 顺序表的底层存储空间是连续的,链表的底层存储空间是不连续的,链表的每个节点需要额外的指针来指向下一个节点,占用更多的存储空间。 顺序表的随机访问性能好,时间复杂度为O(1),链表的随机访问性能…

【C++软件调试技术】C++软件开发维护过程中典型调试问题的解答与总结

目录 1、引发C软件异常的常见原因有哪些? 2、排查C软件异常的常用方法有哪些? 3、为什么要熟悉常见的异常内存地址? 4、调试时遇到调用IsBadReadPtr或者IsBadWritePtr引发的异常,该如何处理? 5、如何排查GDI对象泄…

Oracle 获取最大值,第二大,第三大,第 N 大值

目录 1、原始数据2、获取最大值记录3、获取第二大值记录4、获取第三大值记录 1、原始数据 select * from test_2024_04_15_001 order by 销量 desc,渠道2、获取最大值记录 select 渠道,销量 from ( select a.渠道, a.销量 from test_2024_04_15_001 a order by a.销量 desc,…

Arcade 加载本地文本

参考 https://api.arcade.academy/en/latest/examples/text_loc_example.html#text-loc-example 代码 """ Example showing how to draw text to the screen.If Python and Arcade are installed, this example can be run from the command line with: pyth…

极大似然估计、最大后验估计、贝叶斯估计

机器学习笔记 第一章 机器学习简介 第二章 感知机 第三章 支持向量机 第四章 朴素贝叶斯分类器 第五章 Logistic回归 第六章 线性回归和岭回归 第七章 多层感知机与反向传播【Python实例】 第八章 主成分分析【PCA降维】 第九章 隐马尔可夫模型 第十章 奇异值分解 第十一章 熵…

【随笔】Git 高级篇 -- 获取远程分支数据 git fetch(二十七)

💌 所属专栏:【Git】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! 💖 欢迎大…

代码随想录算法训练营DAY27|C++回溯算法Part.3|39.组合总和、40.组合总和II、组合问题小总结、131.分割回文串

文章目录 39.组合总和思路伪代码实现剪枝优化CPP代码普通版本剪枝版本 40.组合总和II思路伪代码CPP代码不采用used数组的去重方法 组合问题小总结131.分割回文串思路伪代码CPP代码 39.组合总和 力扣题目链接 文章讲解:39.组合总和 视频讲解:Leetcode:39.…

数据结构——顺序表——数组元素和与数字和的绝对差

数据结构——顺序表——数组元素和与数字和的绝对差 class Solution { public:int differenceOfSum(vector<int>& nums) {int a 0;int b 0;for(int i 0; i < nums.size(); i){a a nums[i];while(nums[i]){b b (nums[i] % 10);nums[i] nums[i] / 10;}}retu…

常见的Linux命令

linux操作系统 ctrl鼠标滚动 放大缩小字体 cd /目录进入目录下 ls查看当前目录下的所有内容 tar -zxvf 压缩包名字 对压缩包进行解压 sync将数据由内存同步到硬盘上 shutdown关机指令 shutdown -h 10 /10 表示十分钟后关机 shutdown -h now 表示现在关机 shutdown -h…

OpenCV4.9图像金字塔

目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 pyrUp()和 pyrDown()对给定图像进行下采样或上采样。 理论 注意 下面的解释属于 Bradski 和 Kaehler 的 Learning OpenCV 一书。 通常&#xff0c;我们需要将图像转换为与原始图像不同的大小。为此…