前言
各位好,我是YourBatman。前面用四篇文章介绍完了Jackson底层流式API的读(JsonParser)、写(JsonGenerator)操作,我们清楚的知道,这哥俩都是abstract抽象类,使用时并没有显示的去new它们的(子类)实例,均通过一个工厂来搞定,这便就是本文的主角JsonFactory。
通过名称就知道,这是工厂设计模式。Jackson它并不建议你直接new读/写实例,因为那过于麻烦。为了对使用者屏蔽这些复杂的构造细节,于是就有了JsonFactory实例工厂的出现。
可能有的人会说,一个对象工厂有什么好了解的,很简单嘛。非也非也,一件事情本身的复杂度并不会凭空消失,而是从一个地方转移到另外一个地方,这另外一个地方指的就是JsonFactory。因此按照本系列的定位,了解它你绕不过去。
版本约定
- Jackson版本:2.11.0
- Spring Framework版本:5.2.6.RELEASE
- Spring Boot版本:2.3.0.RELEASE
正文
JsonFactory是Jackson的(最)主要工厂类,用于 配置和构建JsonGenerator和JsonParser,这个工厂实例是线程安全的,因此可以重复使用。
作为一个实例工厂,它最重要的职责当然是创建实例对象。本工厂职责并不单一,它负责读、写两种实例的创建工作。
创建JsonGenerator实例
JsonGenerator它负责向目的地写数据,因此强调的是目的地在哪?如何写?
如截图所示,一共有六个重载方法用于构建JsonGenerator实例,多个重载方法目的是对使用者友好,我们可以认为最终效果是一样的。比如,底层实现是:
JsonFactory: @Override public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { IOContext ctxt = _createContext(out, false); ctxt.setEncoding(enc); // 如果编码是UTF-8 if (enc == JsonEncoding.UTF8) { return _createUTF8Generator(_decorate(out, ctxt), ctxt); } // 使用指定的编码把OutputStream包装为一个writer Writer w = _createWriter(out, enc, ctxt); return _createGenerator(_decorate(w, ctxt), ctxt); }
这就解释了,为何在详解JsonGenerator的这篇文章中,我一直以UTF8JsonGenerator
作为实例进行讲解,因为例子中指定的编码就是UTF-8嘛。当然,即使你自己不显示的指定编码集,默认情况下Jackson也是使用UTF-8:
JsonFactory: @Override public JsonGenerator createGenerator(OutputStream out) throws IOException { return createGenerator(out, JsonEncoding.UTF8); }
示例:
@Test public void test1() throws IOException { JsonFactory jsonFactory = new JsonFactory(); JsonGenerator jsonGenerator1 = jsonFactory.createGenerator(System.out); JsonGenerator jsonGenerator2 = jsonFactory.createGenerator(System.out, JsonEncoding.UTF8); System.out.println(jsonGenerator1); System.out.println(jsonGenerator2); }
运行程序,输出:
com.fasterxml.jackson.core.json.UTF8JsonGenerator@cb51256 com.fasterxml.jackson.core.json.UTF8JsonGenerator@59906517
创建JsonParser实例
JsonParser它负责从一个JSON字符串中提取出值,因此它强调的是数据从哪来?如何解析?
如截图所示,一共11个重载方法(其实最后一个不属于重载)用于构建JsonParser实例,它的底层实现是根据不同的数据媒介,使用了不同的处理方式,最终生成UTF8StreamJsonParser/ReaderBasedJsonParser。
你会发现这几个重载方法均无需我们指定编码集,那它是如何确定使用何种编码去解码形如byte[]数组这种数据来源的呢?这得益于其内部的编码自动发现机制实现,也就是ByteSourceJsonBootstrapper#detectEncoding()这个方法。
示例:
@Test public void test2() throws IOException { JsonFactory jsonFactory = new JsonFactory(); JsonParser jsonParser1 = jsonFactory.createParser("{}"); // JsonParser jsonParser2 = jsonFactory.createParser(new FileReader("...")); JsonParser jsonParser3 = jsonFactory.createNonBlockingByteArrayParser(); System.out.println(jsonParser1); // System.out.println(jsonParser2); System.out.println(jsonParser3); }
运行程序,输出:
com.fasterxml.jackson.core.json.ReaderBasedJsonParser@5f3a4b84 com.fasterxml.jackson.core.json.async.NonBlockingJsonParser@27f723
创建非阻塞实例
值得注意的是,上面截图的11个方法中,最后一个并非重载。它创建的是一个非阻塞JSON解析器,也就是NonBlockingJsonParser,并且它还没有指定入参(数据源)。
NonBlockingJsonParser是Jackson在2.9版本新增的的一个解析器,目标是进一步提升效率、性能。但它也有局限的地方:只能解析使用UTF-8编码的内容,否则抛出异常。
当然喽,现在UTF-8编码几乎成为了标准编码手段,问题不大。但是呢,我自己玩了玩NonBlockingJsonParser,发现复杂度增加不少(玩半天才玩明白😄),效果却并不显著,因此这里了解一下便可,至少目前不建议深入探究。
小贴士:不管是Spring还是Redis的反序列化,使用的均是普通的解析器(阻塞IO)。因为JSON解析过程从来都不会是性能瓶颈(特殊场景除外)