5. JsonFactory工廠而已,還蠻有料,這是我沒想到的
阿新 • • 發佈:2020-08-20
> 少年易學老難成,一寸光陰不可輕。本文已被 [**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`,發現複雜度增加不少(玩半天才玩明白