Jackson 2.x 系列【19】模块 Module

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

本系列Jackson 版本 2.17.0

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

1. 前言

Jackson三大核心模块支持标准的JDK类和自定义Java Bean对象和Json之间的互相转换,当需要扩展以支持其他数据类型时,可以使用Module构建自定义的模块并注册到ObjectMapper中。

Jackson已经提供了多个扩展模块,在Jackson 2.x 系列【1】概述中已经有讲述过,接下来讲解Module并实现注册自定义模块。

2. 核心类

2.1 Module

Module抽象类用于在ObjectMapper中注册扩展,提供了一套定义良好的扩展功能,例如对新数据类型的支持、自定义的序列化/反序列化逻辑等。

定义了三个方法,用于返回模块名称、版本、类型ID,这些模块的基础信息是必须可少的,在实现抽象类时,需要提供这些信息:

java 复制代码
    public abstract String getModuleName();

    @Override
    public abstract Version version();

    public Object getTypeId() {
        return getClass().getName();
    }

Module包含了一个SetupContext内部接口,一般由ObjectMapper去实现并调用setupModule方法执行真正的注册逻辑:

java 复制代码
    public abstract void setupModule(SetupContext context);

    public static interface SetupContext
    {
        // 省略...........

2.2 SimpleModule

SimpleModuleModule的默认实现,提供了注册序列化/反序列化器、Bean序列化/反序列化器修饰器、子类型、Mixin等功能,也可添加自定义的AbstractTypeResolverValueInstantiator。在创建自定义模块时,可以直接使用或者继承该类来实现。

注意事项:

  • 注册的序列化器和反序列化器都会被作为默认的,优先级会低于注解指定的
  • 子类实现中,不推荐重写#setupModule(SetupContext),如果重写了必须调用super.setupModule(context)确保注册操作能够正常执行
  • 在注册JsonSerializerJsonDeserializer时,仅比较类型擦除后的Class,所有泛型参数不要使用CollectionMap等结构化的类型,不然处理时会抛出异常

SimpleModule包含了多个成员属性:

java 复制代码
    // 名称
    protected final String _name;
    // 版本
    protected final Version _version;

    // 序列化器/反序列化器
    protected SimpleSerializers _serializers = null;
    protected SimpleDeserializers _deserializers = null;
    
    // Key 序列化器/反序列化器
    protected SimpleSerializers _keySerializers = null;
    protected SimpleKeyDeserializers _keyDeserializers = null;
    
    // Bean 序列化/反序列化装饰器
    protected BeanDeserializerModifier _deserializerModifier = null;
    protected BeanSerializerModifier _serializerModifier = null

提供了多个添加注册内容的方法:

3. 案例演示

3.1 自定义模块

在创建自定义模块时,可以直接使用SimpleModule创建,或者继承来实现。

例如直接创建:

java 复制代码
        // 创建版本
        Version version = VersionUtil.parseVersion("1.0.0", "com.pearl", "jackson-datatype-my");
        // 创建模块
        SimpleModule myModule = new SimpleModule("myModule", version);
        // 添加序列化器/反序列化器
        myModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        myModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

例如继承实现:

java 复制代码
public class MyModule extends SimpleModule {

    private static final long serialVersionUID = 1L;

    public static final Version VERSION = VersionUtil.parseVersion("1.0.0", "com.pearl", "jackson-datatype-my");

  public MyModule() {
        super("myModule",VERSION);
    }
    
    public void setupModule(Module.SetupContext context) {
        // 必须调用父类的
        super.setupModule(context);
        
        // 添加序列化器
        SimpleDeserializers desers = new SimpleDeserializers();
        desers.addDeserializer(ZoneOffset.class, JSR310StringParsableDeserializer.ZONE_OFFSET);
        context.addDeserializers(desers);

        // 添加反序列化器
        SimpleSerializers sers = new SimpleSerializers();
        sers.addSerializer(ZoneOffset.class, new ToStringSerializer(ZoneOffset.class));
        context.addSerializers(sers);
    }
}

3.2 注册

3.2.1 API

ObjectMapper提供了多种注册模块的方法:

java 复制代码
    public ObjectMapper registerModule(Module module);
    public ObjectMapper registerModules(Module... modules) ;
    public ObjectMapper registerModules(Iterable<? extends Module> modules) ;

代码示例:

java 复制代码
        ObjectMapper objectMapper = new ObjectMapper();
        // 创建版本
        Version version = VersionUtil.parseVersion("1.0.0", "com.pearl", "jackson-datatype-my");
        // 创建模块
        SimpleModule myModule = new SimpleModule("myModule", version);
        // 添加序列化器/反序列化器
        myModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        myModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 注册
        objectMapper.registerModule(myModule);

3.2.2 SPI

Jackson也提供了SPI的方式进行注册,适用于独立的第三方模块,例如jackson-datatype-jsr310就是使用的这种方式。

示例,Idea创建一个模块,使用SPI机制加载自定义模块类: 其他项目引入该模块:

xml 复制代码
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>jackson-datatype-my</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

查询所有注册的模块:

java 复制代码
        objectMapper.findAndRegisterModules();// SPI发现并注册
        Set<Object> moduleIds = objectMapper.getRegisteredModuleIds();
        System.out.println(moduleIds);

输出如下:

java 复制代码
[jackson-datatype-jsr310, myModule]

3.3 查询

ObjectMapper提供了两个静态方法用于查询注册的模块对象:

java 复制代码
    public static List<Module> findModules() ;
    public static List<Module> findModules(ClassLoader classLoader) ;

查询结果如下:

相关推荐
咖啡啡不加糖11 分钟前
RabbitMQ 消息队列:从入门到Spring Boot实战
java·spring boot·rabbitmq
玩代码18 分钟前
Java线程池原理概述
java·开发语言·线程池
NE_STOP21 分钟前
SpringBoot--如何给项目添加配置属性及读取属性
java
水果里面有苹果24 分钟前
20-C#构造函数--虚方法
java·前端·c#
%d%d227 分钟前
python 在运行时没有加载修改后的版本
java·服务器·python
金銀銅鐵33 分钟前
[Kotlin] 单例对象是如何实现的?
java·kotlin
泰勒疯狂展开34 分钟前
Java研学-MongoDB(三)
java·开发语言·mongodb
zzywxc78741 分钟前
AI技术通过提示词工程(Prompt Engineering)正在深度重塑职场生态和行业格局,这种变革不仅体现在效率提升,更在重构人机协作模式。
java·大数据·开发语言·人工智能·spring·重构·prompt
张先shen1 小时前
Elasticsearch RESTful API入门:索引的增删改查完全指南
java·大数据·elasticsearch·搜索引擎·架构·全文检索·restful
天天摸鱼的java工程师1 小时前
工作八年,如果现在让我重做“教务系统”毕业设计,我会这样答...
java·后端