推薦幾個開源類庫,超好用,遠離996!

四猿外發表於2021-11-01

今天給大家分享幾個 Java 的開源類庫,親測非常好用!

有了它們之後,你就可以和很多重複勞動說再見了。

1. MapStruct

MapStruct是幹什麼的?

MapStruct是個程式碼產生器,它能直接根據註解生成 Java 物件對應的轉換器。

比如,直接把一個 A 型別的 Java 物件,給轉成 B 型別的 Java 物件,只需要在他們之間配置上欄位之間的對映關係即可。

為什麼在專案裡用它?

現在隨便一個專案都是多層的,尤其是 Web 專案,經常需要在多層之間做物件模型轉換,比如 DTO 轉換成 BO。

DTO(Data Transfer Object):資料傳輸物件,Service 向外傳輸的物件。
BO(Business Object):業務物件,由 Service 層輸出的封裝業務邏輯的物件。

但是這種轉換工作就像是小時候,老師罰我們抄寫名人名言 100 遍一樣,十分枯燥,還容易出錯。

像這樣:

public class CarMapper {
    CarDto carDoToCarDto(Car car) {
        CarDto carDto = new CarDto();
        carDto.setCarId(car.getCarId());
        carDto.setWheel(car.getWheel());
        carDto.setCarType(car.getCarType());
        carDto.setCarColor(car.getCarColor());
        ......
    }

}

要是 Car 有幾十個欄位,像 Car 一樣的又有幾十個類,你可以想一下,這種繁瑣程度。

在 MapStruct 之前,我們都是通過 Apache 或者 Spring 的 BeanUtils 工具,去自動做這種事情。

但是這類工具有兩個問題:

1.效能比較差

效能差主要是 Apache 的 BeanUtils 這套東西,它每次都要針對欄位,做是否可讀寫的檢查,還要根據欄位生成對應的 PropertyDescriptor。

這些嚴重影響了它的效能,所以,在阿里 Java 手冊裡,也不推薦用它。

Spring 的 BeanUtils,雖然精簡了很多 Apache 的 BeanUtils 的讀寫檢查以及對應的屬性資訊記錄,但是它依然是通過反射呼叫,而且是大量反射呼叫。這種效能也不能令人滿意。

2.執行期做轉換,出錯就代表損失

BeanUtils 這類工具,有個統一的名稱,叫做 Java 物件對映框架。

它們大部分的實現都是在執行期去執行程式碼,然後在 Java 物件之間去拷貝對應的值。

執行期間做這種事兒,有個最大的問題——整個專案啟動執行後,才能發現錯誤。比如,轉換的時候,型別不一致導致報錯。

對於此種情況,我們們大家都知道,這事兒就像開業酬賓沒搞好,變成了開業仇賓……

如果能寫完程式碼,編譯的時候就發現問題,這種損失就可以避免了。

MapStruct 的引入就是為了解決以上這兩個問題。

MapStruct 首先是個程式碼產生器,它是根據註解,去產生一個專門用來轉換的工具類,這個工具類,就像我們自己寫的 Java 類一樣,可以直接被使用,這樣就避免了反射。

同時,它產生的轉換類也特別簡單,就是預設會在兩個型別的 Java 物件之間,拷貝同名屬性的值。

如果有了配置,屬性不同名也可以拷貝。所以它的效能很好。

示例程式碼如下:

@Mapper
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carDoToCarDto(Car car);
}

MapStruct由於是個程式碼產生器,就帶來了個巨大的好處,就是這傢伙是在編譯階段就會生成對應的類,所以,如果有了類似型別轉換不過去的問題,直接就編譯報錯了,根本等不到執行才發現。這樣的話,就不會造成什麼損失,這真是件十分 Nice 的事情。

程式碼庫地址

https://github.com/mapstruct/mapstruct

2. Retrofit

Retrofit 是幹什麼的?

Retrofit 就是一套 Http 客戶端,可以用來訪問第三方的 Http 服務。

比如,我們們程式碼裡想呼叫一個 Http 協議的 URL,就可以用它來訪問這個 URL,獲取響應結果。

為什麼在專案裡用它?

在公司裡,我們有些專案有如下的特點:

  1. 不是基於 Spring 的專案
  2. 需要經常訪問大量的第三方 Http 服務
  3. 訪問 Http 服務的模型通常是非同步回撥

以前的時候,我們訪問 Http 服務,都是直接用的 HttpClient。

可是吧,HttpClient 用起來實在夠麻煩的。主要也存在兩個問題:

1.請求引數和 URL 拼接實在繁瑣

請求引數和 URL 拼接實在是太煩人了。你想想,每呼叫一個介面,就需要自己去拼接引數,有的 URL,甚至十幾二十個引數需要拼接。

拼接這事兒簡單、枯燥、重複,還沒有技術含量,但是工作量卻不小,時間真的算浪費了。

URIBuilder uriBuilder = new URIBuilder(uriBase);
uriBuilder.setParameter("a", "valuea");
uriBuilder.setParameter("b", "valueb");
uriBuilder.setParameter("c", "valuec");
uriBuilder.setParameter("d", "valued");
uriBuilder.setParameter("e", "valuee");
uriBuilder.setParameter("f", "valuef");
uriBuilder.setParameter("g", "valueg");
uriBuilder.setParameter("h", "valueh");
uriBuilder.setParameter("i", "valuei");
...

2.非同步回撥需要自己搞

非同步回撥這種模型不好處理,主要就是需要自己去搞執行緒池,還要對執行緒池管理,還要考慮出錯的重試之類的容錯問題,實在麻煩。

所以,我們就需要一套能用法簡單,不用我們一直搞拼接引數,自己搞執行緒管理就能完成對第三方 Http 服務訪問的庫。

其實我們也想過用 Feign 這套框架的。但是,這套東西和 Spring 繫結的太緊了。如果離開 Spring,它的一些功能就沒法簡單的通過註解直接使用,必須自己寫程式碼呼叫。

而且,Feign 要實現非同步回撥方式使用,尤其在協程方面,還是需要自己開發。

這時候,Retrofit 就跳進了我們的選型裡。

Retrofit 的模型裡,非同步回撥模型它支援的很好,我們只需要實現一個 Callable 就夠了。

並且最清爽的是,它和 Spring 沒什麼關係。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://xxx.example.com/")
        .build();

public interface BlogService {
    @GET("blog/{id}")
    Call<ResponseBody> getBlog(@Path("id") int id);
}

BlogService service = retrofit.create(BlogService.class);

Call<ResponseBody> call = service.getBlog(2);
// 用法和OkHttp的call如出一轍,
// 回撥
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        try {
            System.out.println(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        t.printStackTrace();
    }
});

你看,只需要寫上這些程式碼,我們就不需要操心惱人的 Url 拼接和非同步回撥的管理問題了。全交給了 Retrofit,著實推薦。

程式碼庫地址

https://github.com/square/retrofit

3. Faker

Faker 是幹什麼的?

Faker 是專門用來產生各種假資料的輔助工具庫。

比如,你想產生個和真實資料一樣的有姓名、有地址的使用者。

為什麼在專案裡用它?

我們經常需要造資料去測試,但是,如果沒有工具輔助,我們自己造資料,存在一些問題。

1.資料是需要格式的

很多關於專案,都需要一些格式上儘量能模模擬實世界的資料。

比如,國內使用者的姓名,大部分都是兩字、三字的姓名,叫王大,就不能叫 王da 這種。

又比如,國內的地址是 xx市xx區xx街道xx號 這種的,就不能胡寫一個幾個沒意義的漢字來當地址。

用貼近真實格式的資料,一來可以測出我們對使用者的資料解析是否存在問題,二來可以測出資料庫內的欄位長度是否沒問題。

所以,格式對產生出可靠地測試結果,是很重要的。

2.資料的量大

有的測試資料量都是上十萬、百萬的,這些量級的資料並不是只會產生一次。

甚至幾乎每個專案,每個專案的每次測試,可能都會需要新的資料,需要能源源不斷地產生出來。

更甚至的是,有時候還想要根據我們的要求,在恰當的時候,產生某種關係的資料,或者以某些特定頻率產生。比如,兩秒後產生一次資料;比如,產生一批姓王的資料。

以上這三種要求綜合起來,要是我們自己造資料,那真是要了命了。

與其自己開發,不如用現成的——Faker 庫被我們找到了。

Faker庫可以創造三百多種資料,而且還很容易對它進行擴充套件改造,去產生更多的貼合我們需求的資料。

Faker faker = new Faker();

String name = faker.name().fullName(); // Miss Samanta Schmidt
String firstName = faker.name().firstName(); // Emory
String lastName = faker.name().lastName(); // Barton

String streetAddress = faker.address().streetAddress(); // 60018 Sawayn Brooks Suite 449

幾行程式碼,我們需要的一個使用者就有了。

用上 Faker 後,小夥伴們紛紛表示“有更多的時間摸魚了”。

程式碼庫地址

https://github.com/DiUS/java-faker

4. Wiremock

Wiremock 是幹什麼的?

Wiremock 是一個可以模擬服務的測試框架。

比如,你想測試訪問阿里的支付相關介面的程式碼邏輯,就可以用它來做測試。

為什麼在專案裡用它?

比如,我們需要呼叫銀行介面去做資金業務,呼叫微信介面去做微信登入……這些呼叫第三方服務的測試存在一個問題:

即太過依賴對方的平臺。假如對方平臺限制了一些 IP,或者限制了訪問頻率,又或者就是服務出現了維護,都會影響我們自身的功能測試。

為了解決上述問題,在之前,我們需要自己寫程式碼模仿第三方的介面,等我們自己全部測試沒問題了,再去和第三方聯調。對於這種模擬出來的介面,我們稱作擋板。

可是,這種方式是個苦活,沒人願意幹。因為每接入一個第三方,可能都需要做擋板。辛苦做個擋板,就是單純為了測試。如果第三方的介面做了改造,你這邊還得跟著改。

大家可以想想,換成你自己,你願意做這麼件事兒嗎?

這時候,Wiremock 的價值就體現出來了。有了 Wiremock,擋板這種東西就再也不存在了,直接在單元測試裡模擬測試即可,像這樣:

WireMock.stubFor(get(urlPathMatching("/aliyun/.*"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", APPLICATION_JSON)
                        .withBody("\"testing-library\": \"WireMock\"")));

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet(String.format("http://localhost:%s/aliyun/wiremock", port));
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);

verify(getRequestedFor(urlEqualTo(ALIYUN_WIREMOCK_PATH)));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals(APPLICATION_JSON, httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("\"testing-library\": \"WireMock\"", stringResponse);

程式碼庫地址

https://github.com/wiremock/wiremock

結語

雖然 Java 有很多遭人詬病的地方,但是 Java 最重要的優點之一,就是它的生態,有其琳琅滿目的各種工具類庫。

希望大家都“懶”一點,不要埋頭去做無效的苦幹,不要自己造輪子,你要相信:

你遇到的問題,基本已經有很多人遇到過了,而且已經被牛人給解決了,把輪子都給你造好了。


你好,我是四猿外。

一家上市公司的技術總監,管理的技術團隊一百餘人。

我從一名非計算機專業的畢業生,轉行到程式設計師,一路打拼,一路成長。

我會把自己的成長故事寫成文章,把枯燥的技術文章寫成故事。

歡迎關注我的公眾號,關注後可以領取高併發、演算法刷題筆記、計算機高分書單等學習資料。

相關文章