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;
        // 查询 @JsonGetter
        JsonGetter 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 NopAnnotationIntrospector
        extends AnnotationIntrospector
        implements java.io.Serializable {
        
    private static final long serialVersionUID = 1L;
	
	// 内部实例
    public final static NopAnnotationIntrospector instance = new NopAnnotationIntrospector() {
        private static final long serialVersionUID = 1L;

        @Override
        public Version version() {
            return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
        }
    };

    @Override
    public Version version() {
        return Version.unknownVersion();
    }
}

6. 案例演示

6.1 关闭注解功能

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

例如,下面的POJO类:

public class IntrospectorVO {

    @JsonIgnore
    public 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 {

    @Override
    protected 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();


    @Override
    public Object findSerializer(Annotated a) {
        JsonListToCommaSplitString ann = (JsonListToCommaSplitString) this._findAnnotation(a, JsonListToCommaSplitString.class);
        if (ann != null) {
            return ListToStringJsonSerializer.instance;
        }
        return null;
    }

    @Override
    public 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);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL12 获取每个部门中当前员工薪水最高的相关信息

题目:获取每个部门中当前员工薪水最高的相关信息 注意了,这道题目,分组函数只能查出来:每个部门的最高薪水,group by dept_no ,根据部门分组,绝对不能group by dept_no,emp_no,不能…

【刷题笔记】第二天

一道图论相关的题目 3108. 带权图里旅途的最小代价 结论: 做与运算,结果不会大于当前值,也就是说与运算只能导致结果不变或越来越小,所以要使得边的and值最小,就是把每一个联通块的所有边都and一遍。 方法1&#xf…

Vue项目实战:基于用户身份的动态路由管理

🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…

201403-3 命令行选项

100分 #include <bits/stdc.h> using namespace std;int main() {string line;cin >> line;map<char, bool> dct; // true:带参数 false:不带参数for (int i 0; i < line.size(); i){if (line[i] ! :){dct.insert(pair<char, bool>(line[i], fals…

ubuntu 23.10.1 mysql 安装

注&#xff1a;请进入root用户模式下操作&#xff0c;若没有&#xff0c;输入命令前加上sudo 1、更新软件包列表 apt update2、安装最新版的Mysql服务器 apt install mysql-server -y如果不加-y 会在安装过程中&#xff0c;系统将提示你设置MySQL的root密码。确保密码足够强…

职场成长之路:如何规划与实现

在职场中&#xff0c;每个人都希望实现自己的职业目标和成长。然而&#xff0c;职场成长并非一蹴而就&#xff0c;需要有明确的规划和方法。本文将探讨如何在职场中规划与实现成长&#xff0c;帮助您迈向成功之路。 一、明确职业目标 1. 自我认知&#xff1a;了解自己的兴趣、…

C语言双向链表

1. 链表的分类 链表的种类很多&#xff0c;主要由三个要素决定&#xff1a;是否带头&#xff0c;单向还是双向&#xff0c;是否循环。 根据这三个要素的组合&#xff0c;共可得到8&#xff08;2*2*2&#xff09;种链表 而我们常用的链表有两种&#xff1a; 1. 单链表&#xf…

鸿蒙原生应用元服务-访问控制(权限)开发Stage模型向用户申请授权

一、向用户申请授权 当应用需要访问用户的隐私信息或使用系统能力时&#xff0c;例如获取位置信息、访问日历、使用相机拍摄照片或录制视频等&#xff0c;应该向用户请求授权。这需要使用 user_grant 类型权限。在此之前&#xff0c;应用需要进行权限校验&#xff0c;以判断当前…

02_按键控制LED

按键控制LED 按键控制LED 按键控制LED while (1){//按键控制LEDif(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)GPIO_PIN_RESET)//读取PC5引脚状态&#xff0c;即检测按键是否按下{while(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)GPIO_PIN_RESET);//松手检测HAL_GPIO_WritePin(GPIOA,GPIO_PI…

【JavaWeb】Day45.Mybatis——入门程序

什么是MyBatis? MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发。 &#xff08;持久层&#xff1a;指的是就是数据访问层(dao)&#xff0c;是用来操作数据库的。&#xff09; &#xff08;框架&#xff1a;是一个半成品软件&#xff0c;是一套可重用的、通用…

【软考】UML中的图之类图

目录 1. 说明2. 图示3. 类图使用方式3.1 对系统的词汇建模3.2 对简单的协作建模3.3 对逻辑数据库模式建模 1. 说明 1.类图&#xff08;Class Diagram&#xff09;展现了一组对象、接口、协作和它们之间的关系。2.在面向对象系统的建模中所建立的最常见的图是类图。3.类图给出系…

Openwrt21.02支持SKW78(MT7621)

1.获取SDK 1.下载Openwrt源码 下载链接&#xff1a; git clone --branch openwrt-21.02 https://gitee.com/cocos_yang/openwrt.git 下载完后&#xff0c;会有一个openwrt目录&#xff0c;进入openwrt目录 cd openwrt 修改feeds.conf.default的内容&#xff0c;如下所示&#x…

操作系统(1)计算机存储结构

文章目录 一、计算机存储结构1、基本概念2、计算机存储结构的组成部分3、局部性原理4、高速缓存4.1、基本概念4.2、工作原理4.3、层次结构4.4、对系统性能的影响4.5、一致性问题 5、寄存器5.1&#xff0c;种类5.2&#xff0c;特点5.3、作用5.4、寄存器与缓存的差异 前言 计算机…

MybatisPlus实现数据权限隔离

引言 Mybatis Plus对Mybatis做了无侵入的增强&#xff0c;非常的好用&#xff0c;今天就给大家介绍它的其中一个实用功能&#xff1a;数据权限插件。 数据权限插件的应用场景和多租户的动态拦截拼接SQL一样。建议点赞收藏关注&#xff0c;方便以后复习查阅。 依赖 首先导入M…

【网安小白成长之路】7.burp基本使用

&#x1f42e;博主syst1m 带你 acquire knowledge&#xff01; ✨博客首页——syst1m的博客&#x1f498; &#x1f51e; 《网安小白成长之路(我要变成大佬&#x1f60e;&#xff01;&#xff01;)》真实小白学习历程&#xff0c;手把手带你一起从入门到入狱&#x1f6ad; &…

计算机网络3——数据链路层1

文章目录 一、介绍1、基础2、内容 二、数据链路层的几个共同问题1、数据链路和帧2、三个基本问题1&#xff09;封装成帧2&#xff09;透明传输3&#xff09;差错检测 三、点对点协议 PPP1、PPP协议的特点1&#xff09;PPP 协议应满足的需求2&#xff09;PPP 协议的组成 2、PPP协…

vmware安装ubuntu-18.04系统

一、软件下载 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1fK2kygRdSux1Sr1sOKOtJQ 提取码&#xff1a;twsb 二、安装ubuntu系统 1、把ubuntu-18.04的压缩包下载下来&#xff0c;并且解压 2、打开vmware软件&#xff0c;点击文件-打开 3、选择我们刚刚解…

限制登录Linux服务器的几种方式

一.第一种方法 通过修改TCP Wrappers服务访问控制来实现限制登录Linux 1.这里以sshd服务为例&#xff0c;配置完成后&#xff0c;只允许配置允许的IP才能ssh连接本机服务器&#xff0c;其他IP拒绝判断某一个基于tcp协议的服务是否支持tcp_wrapper&#xff0c;要先判断它是否支…

【C语言】<动态内存管理>我的C语言终末章

&#xff1c;动态内存管理&#xff1e; 1. 为什么要有动态内存分配2. malloc和free2.1 malloc2.2 free 3. calloc和realloc3.1 calloc3.2 realloc 4.常见的动态内存错误4.1 对NULL指针的解引用操作4.2 对动态开辟空间的越界访问4.3 对非动态开辟内存使用free释放4.4 使用free释…

Linux下SPI设备驱动实验:实现SPI发送/接收数据的函数

一. 简介 前面文章介绍了SPI设备数据收发处理流程&#xff0c;后面几篇文章实现了SPI设备驱动框架&#xff0c;加入了字符设备驱动框架代码。文章如下&#xff1a; SPI 设备驱动编写流程&#xff1a;SPI 设备数据收发处理流程中涉及的结构体与函数-CSDN博客 SPI 设备驱动编写…