Go 語言基礎系列(六):包

什麼是包

**包 **是一個重要的概念,我們將詳細介紹,那麼該如何進行思考和學習呢?

思維邏輯圖如下:

下面我們從以上三個方面對 **包 **這一概念進行相應的介紹

**   工作區  **

我們一般提到包的位置指的是項目包的位置,其實項目包是被包含於工作區或者工作目錄

,而工作區或者工作目錄就是我們經常碰到的 GOPATH。比如說,我們創建了一個叫做 myWeb 的項目包,該包是一個關於博客 web 的小項目,這個項目包則被包含於 GOPATH 工作區目錄。

所以我們下面首先對工作區 GOPATH 進行介紹,然後對與 GOPATH 緊密相關的 GOROOT 進行介紹

01

** GOPATH**

當我們在命令行執行 go env 時,會呈現出很多與 go 環境信息相關的內容,其中包括 GOPATH 和 GOROOT,如下

圖中 GOPATH = D:\GoProgram,即是我們的工作區,也就是說 D:\GoProgram 即是當前工作區,那麼我們先對工作區的結構進行介紹。

1)工作區的結構

工作區目標一般包含三個子目錄,分別爲:src 目錄,pkg 目錄以及 bin 目錄。

a) src 目錄:src 目錄就是對我們寫的 go 語言代碼進行保存的地方,就像上文提到的 myWeb 項目,而且 go 語言進行編譯時查找代碼就在這個 src 目錄查找;

b) pkg 目錄:pkg 目錄是對 go get/go install 命令執行後生成的 “.a” 文件進行保存,c 語言中是指編譯後生成的靜態庫文件,當然,go 語言編譯生成的 lib 文件也保存在這裏;

c) bin 目錄:bin 目錄是對 go get/go install 命令執行後生成的二進制可執行文件進行保存,在 windows 系統中就是. exe 結尾的文件;

注:以上三個目錄,對於 pkg 目錄和 bin 目錄不需要我們主動創建,因爲在執行相關命令時比如 go install 會自動創建目錄,但是 src 目錄需要我們主動去創建。
2)工作區的設置

對於工作區來說,可以不止一個,也可以多個。所以,像上文中我們執行 go env 後 GOPATH 只有 D:\GoProgram 一個目錄,其實也可以設置多個目錄。那麼怎麼設置新增的工作區呢?

a) 對於 windows 系統

以 D:\gotest 爲例

設置臨時工作區:set GOPATH=%GOPATH%; D:\gotest;

設置永久工作區:setx GOPATH %GOPATH%; D:\gotest;

b) 對於 linux 系統

以 / home/gotest 爲例

設置臨時工作區:

GOPATH=$GOPATH: /home/gotest;

設置永久工作區:

vi /etc/profile,在末尾處新增  export GOPATH=$GOPATH: /home/gotest,保存退出,執行 source /etc/profile  ;

注:當多個工作區並存時,在查找依賴包時,首先會從 GOROOT 下查找,然後以工作區聲明的順序順序查找,找到則停止,一直都沒有找到便會報錯;  

02

** GOROOT**

GOROOT 即是 Go 語言的安裝目錄,這個目錄我們平常接觸的沒有 GOPATH 多,這裏我們對這個目標下的子目錄進行簡要介紹。安裝目錄下的子目錄如下

a) api 目錄:Go 語言不同版本的一些 api 說明文件;

b) bin 目錄:Go 語言編譯器,godoc 和 gofmt 格式化三個可執行文件;

c) doc 目錄:Go 的文檔介紹,不過是英文的;

d) lib 目錄:被引用的一些庫文件;

e) misc 目錄:一些功能插件,便於對 Go 的查看和編寫;

f) pkg 目錄:主要是 Go 標準庫的一些. a 文件;

g) src 目錄:Go 標準庫文件;
h) test 目錄:Go 語言官方自帶的一些測試文件;

**   包的聲明及導入  **

01

** 包的聲明 **

包的聲明相對比較簡單,主要是通過 Golang 的關鍵字 package 和包的名字結合的方式來聲明,如下:

package main  // 表示聲明瞭一個名爲 main 的包

注:main 包具有特殊性,有且僅有一個,Go 語言會將 main 包編譯爲二進制可執行文件,

02

** 包的導入 **

對於包的導入,均通過關鍵字 import 完成。我們將從以下幾個方面展開:1)本地及遠程導入;2)別名及簡化導入;3)匿名導入;在介紹完包的導入之後,由於此時不止一個包存在,那麼便會涉及到一個問題:多個包的初始化順序問題,所以在 3 種導入方式介紹之後對包的初始化也進行相應的介紹

1)本地及遠程導入

a. 本地導入:本地導入通常是 go 的標準庫,或者是我們自己定義的代碼包

但是對於多個包的導入,通常可以簡化書寫爲:

b. 遠程導入:遠程的包是指需要我們通過遠程服務器獲取的包,比如 github

2)別名及簡化導入

a. 別名導入:顧名思義,即爲導入的包定義一個新的名字

將原來的包名定義爲 mycontext 進行引用

b. 簡化導入:是指在書寫中我們對於包的使用需要引用包名,比如我們需要使用 fmt 包下的 Println() 函數,但是均需要通過 fmt.Println() 的形式進行使用,那麼如何簡化書寫,直接 Println() 書寫呢?

如上,我們可以通過點的導入形式進行簡化書寫

3)匿名導入

匿名導入是指有時候我們需要導入一個包,但是並不對其進行引用使用,所以正常情況下會包 “unused import” 錯誤,爲了不使其報錯,則可以通過匿名的方式導入。不過,因此也會有疑惑,既然不用那爲何還需要導入呢?其實主要是爲了使用該包的 init 函數

該包的匿名導入,主要是爲了使用其 driver.go 裏的 init 函數

通過 init 函數完成對 MySQL 的驅動註冊(將來在 web 開發系列中我們再進行介紹)

4)包的初始化過程

關於包的初始化,會涉及到兩個特殊的函數,分別爲 main 函數和 init 函數。

main 函數:程序的入口,若不存在程序就不能開始執行;

init 函數:主要是一些初始化的工作,先於 main 函數執行;

而這兩個函數的共同點是均由程序自動調用的,通常來講,一個包中可以包含多個 init 函數,但是在 main 包中有且只能有一個 main 函數

關於多個包依次被導入的初始化:我們以 main 包、包 1、包 2 和包 3 爲例。比如 main 包導入包 1,包 1 導入包 2,包 2 又導入包 3,初始化如下圖所示

過程如下:

1)程序從 main 包開始執行①,執行到 import pkg1;

2)執行②導入包 1,繼續執行 import pkg2;

3)執行③導入包 2,繼續執行 import pkg3;

4)執行④導入包 3,由於包 3 沒有繼續導入新的包,所以向下執行⑤對常量和變量的初始化,並執行 init 函數;

5)接下來執行⑥,然後⑦對常量和變量進行初始化並執行 init 函數;

6)之後相同的流程,直到再回到 main 包,對常量變量初始化並執行 main 包的 init 函數,最後執行 main 函數;

**   依賴包管理  **

依賴包管理是指對第三方依賴包的管理,比如從 GitHub 上下載的代碼。那麼爲什麼需要對依賴包進行管理呢?其實是因爲我們在第三方庫下載的代碼不具有持續穩定性,比如一段時間後可能這個庫代碼更新升級了,而且還不向下兼容,更有甚者直接刪除了等等。所以爲了項目的穩定運行,須對依賴包進行管理

下面我們對 golang 依賴包管理的發展歷程進行介紹,主要是三個階段:GOPATH,vendor 和現在的 go mod

01

** GOPATH**

最早的 GOPATH 依賴包管理是將所有的本地代碼和第三方包代碼均放在 GOPATH/src 目錄下,它的優點是統一管理,方便查詢和引用。

但是在使用過程中也會有一個非常嚴重的坑,那就是由於所有不同的項目均放在 GOPATH/src 目錄下,當多個不同項目引用的是同一個第三方包,但是該第三包的不同版本時,便會導致嚴重的包依賴問題。

大家可能會想,在前面工作區配置中,我們不是可以配置多個工作區嗎,我們配置多個工作區,不就可以將本地代碼和第三方包依據不同項目隔離開了嗎,但是怪就怪在,go get 獲取代碼時,當你配置了多個工作區時,它只會下載到第一個工作區目錄

所以這也是 Go 語言之初被詬病的一個點

02

** vendor **

對於依賴包管理的問題,在 GOPATH 中我們也提到,最好的方式是對依賴包根據項目進行隔離。

所以在 go 的 1.5 版本中引入了一個新的屬性:vendor,對應的環境變量是 GO15VENDOREXPERIMENT,默認關閉(設置爲 1 開啓),在 1.6 版本中默認設置爲 1 開啓

vendor 屬性的作用便是:在進行編譯時,優先從 vendor 目錄下查找代碼,如果沒有才回去 GOPATH 下查找,有的話便不會再進行查找

但是同樣 vendor 也有一個問題:vendor 目錄沒有對版本信息進行保存,所以當版本升級,版本追溯都會有困難;而且如何方便的進行依賴包拷貝也是一個問題

爲了解決這些問題,社區裏出現了幾個代表性的工具:godep,golide 和 govendor,這三個工具中 govendor 的使用相對更多一些

主要有如下幾個主要命令:

**init        初始化 vendor 目錄 **

**add       添加依賴包到 vendor 目錄
**

**update  更新依賴包到 vendor 目錄
**

remove 從 vendor 目錄中刪除依賴包

03

** go mod **

go mod 的出現結束了依賴包管理的混亂現象。go mod 是 go 1.11 版本出現的,對應的環境變量是:GO111MODULE(on 代表開啓)。go mod 的出現使依賴包的管理變的更爲簡單,而且可以獨立 GOPATH 和 vendor 存在。

go mod 依賴包管理方式有兩個文件:go.mod 和 go.sum

1)go.mod:主要用來記錄依賴包和依賴包信息

如上圖所示:

a. module  定義項目包名

b. Require 定義依賴包以及版本

c. Indirect  表示是間接引用

2)go.sum:主要用來記錄每個依賴庫的版本和哈希值,便於驗證和校驗

go.sum 的形式爲:

module 版本號 / go.mod h1:哈希值

其中 module 是依賴的路徑,version 是依賴包的版本號,hash 是以 h1: 開頭的字符串

go mod 主要的命令有:

init        將當前文件夾初始化爲新的 module,生成 go.mod 文件

tidy       對丟失的 module 就行導入,對多餘的刪除

vendor  將依賴複製到 vendor 目錄下

以上是依賴包管理的發展歷程,主要是爲了理解來龍去脈增進理解。現在在使用中,我們優先使用 go mod

到此關於 Golang 包的分享就結束了~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/rT-Mnl7jUlo9tSJVPMdQeQ