1. 程式人生 > >5. JsonFactory工廠而已,還蠻有料,這是我沒想到的

5. JsonFactory工廠而已,還蠻有料,這是我沒想到的

> 少年易學老難成,一寸光陰不可輕。本文已被 [**https://www.yourbatman.cn**](https://www.yourbatman.cn) 收錄,裡面一併有Spring技術棧、MyBatis、JVM、中介軟體等小而美的**專欄**供以免費學習。關注公眾號【**BAT的烏托邦**】逐個擊破,深入掌握,拒絕淺嘗輒止。 [TOC] ![](https://img-blog.csdnimg.cn/20200728142302514.png) # 前言 各位好,我是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例項 ![](https://img-blog.csdnimg.cn/20200725145602371.png) JsonGenerator它負責向目的地寫資料,因此強調的是**目的地在哪?如何寫?** 如截圖所示,一共有六個過載方法用於構建JsonGenerator例項,多個過載方法目的是對使用者友好,我們可以認為最終效果是一樣的。比如,底層實現是: ```java 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: ```java JsonFactory: @Override public JsonGenerator createGenerator(OutputStream out) throws IOException { return createGenerator(out, JsonEncoding.UTF8); } ``` 示例: ```java @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); } ``` 執行程式,輸出: ```java com.fasterxml.jackson.core.json.UTF8JsonGenerator@cb51256 com.fasterxml.jackson.core.json.UTF8JsonGenerator@59906517 ``` ## 建立JsonParser例項 ![](https://img-blog.csdnimg.cn/20200725145457812.png) JsonParser它負責從一個JSON字串中提取出值,因此它強調的是**資料從哪來?如何解析?** 如截圖所示,一共11個過載方法(其實最後一個不屬於過載)用於構建JsonParser例項,它的底層實現是根據不同的資料媒介,使用了不同的處理方式,最終生成`UTF8StreamJsonParser/ReaderBasedJsonParser`。 你會發現這幾個過載方法均無需我們指定編碼集,那它是如何確定**使用何種編碼**去解碼形如byte[]陣列這種資料來源的呢?這得益於其內部的編碼自動發現機制實現,也就是`ByteSourceJsonBootstrapper#detectEncoding()`這個方法。 示例: ```java @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); } ``` 執行程式,輸出: ```java 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`,發現複雜度增加不少(玩半天才玩明白