
1、概述
大多數語言都有“依賴”、“包”等概念,Go語言的依賴處理經歷了幾次變革
最早的時候,Go所依賴的所有的第三方庫都放在GOPATH這個目錄下面
從v1.5開始開始引入vendor模式,如果項目目錄下有vendor目錄,那么go工具鏈會優先使用vendor內的包進行編譯、測試等
從v1.11開始,引入了Go Module 作為依賴解決方案,到v1.14宣布Go Module已經可以用於生產環境,到v1.16版本開始Go Module默認開啟
2、GOPATH介紹
2.1 GOPATH目錄
GOPATH是什么,輸入如下命令查看
# go env GOPATH
GOPATH="/Users/ssgeek/go"
進入到該目錄下,目錄結構如下
# cd `go env GOPATH`
# tree -L 2 .
.
├── bin
│ ├── dlv
│ ├── go-outline
│ ├── gomodifytags
│ ├── gopkgs
│ ├── goplay
│ ├── gopls
│ ├── gotests
│ ├── impl
│ └── staticcheck
├── pkg
│ ├── mod
│ └── sumdb
└── src
└── github.com
...
三個目錄中存放的文件說明如下
bin //用來存放編譯后的可執行文件
pkg //用於存放編譯后生成的歸檔文件
src //用來存放go源碼文件
2.2 GOPATH的缺點
在使用GOPATH的模式下,我們需要將應用代碼存放在固定的$GOPATH/src目錄下,並且如果執行go get來拉取外部依賴會自動下載並安裝到$GOPATH目錄下
第三方套件只要不是官方庫,都需要放置在GOPATH/src的路徑下才可以使用
go get最常用在當我們想用別人公開在GitHub上的套件,可以幫我們從網絡clone到GOPATH/src里面。雖然這樣很方便,但是會發現GOPATH/src下會很復雜,除了有你自己開發的代碼目錄,同時也包含其他第三方庫的專屬目錄
我們給不同的項目設置不同的GoPath,優點非常明顯:
便於管理項目,每個項目都是不同的GoPath,這對於我們管理多個Golang項目而言,能夠非常清晰的處理項目結構。如果我們把所有項目都放在同一個GoPath的src包下,那么項目的結構就會變得非常混亂,難以管理
但是當我們需要依賴第三方的包的時候,不同的項目設置不同的GoPath的缺點也非常明顯:
- 第三方依賴的包和我們自己的
Golang包混在一起,會給我們的項目文件管理帶來一定的麻煩 - 不同的
GoPath都需要下載依賴,那么磁盤中重復的依賴就會非常多,會占用我們大量的磁盤空間
3、GO Module介紹
為了解決GOPATH的問題,因此官方在1.11開始推出了Go Modules的功能。Go Modules解決方式很像是Java看到Maven的做法,將第三方庫儲存在本地的空間,並且給項目代碼去引用
3.1 設定GO111MODULE環境變量
總共可以設置三種不同的值
- auto
默認值,go命令會根據當前目錄來決定是否啟用modules功能。需要滿足兩種情形:
該目錄不在GOPATH/src/下
當前或上一層目錄存在go.mod文件
- on
go命令會使用modules,而不會去GOPATH目錄下查找。
- off
go命令將不會支持module功能,尋找依賴按照以前GOPATH的做法去尋找
目前1.16版本默認將這個參數設置成on,而且可能之后的版本會棄用掉GO111MODULE,因此建議要開發Go項目時就不再使用GOPATH了,而是采用Go Modules的做法,因此建議都設定為on
采用Go Modules,下載下來的第三方依賴就位於GOPATH/pkg/mod目錄下
3.2 初始化mod
go mod init <module name>
<module name>可填可不填,不填的話預設就是默認的文件名稱go.mod
在此文件中可以寫以下幾個關鍵字:
- module
定義模組路徑
- go
定義go語言version
- require
指定依賴,預設是最新版,可以指定版本號
- exclude
排除該依賴和其版本
- replace
使用不同的依賴版本並替換原有的依賴版本注解
indirect代表被間接導入的依賴包
假設現在我要引入GitHub上的gin-gonic/gin的依賴,如下定義:
module myProject
go 1.16
require github.com/gin-gonic/gin v1.6.3
再執行以下指令:
go mod xxx
會將需要的依賴安裝在GOPATH/pkg/mod目錄里面
gin@v1.4.0 gin@v1.6.3 gin@v1.7.1
除了go.mod之外,go命令還維護一個名為go.sum的文件,其中包含特定模塊版本內容的預期加密哈希
go命令使用go.sum文件確保這些模塊的未來下載檢索與第一次下載相同,以確保項目所依賴的模塊不會出現意外更改,無論是出於惡意、意外還是其他原因。 go.mod和go.sum都應檢入版本控制。
go.sum不需要手工維護,所以可以不用太關注
只要有開啟go modules功能,go get 就不會像以前一樣在GOPATH/src下放置依賴,而是會放在GOPATH/pkg/mod里面,並且go.mod會寫好引入
3.3 go mod命令
常用
go mod init:初始化go mod, 生成go.mod文件,后可接參數指定 module 名,上面已經演示過。
go mod download:手動觸發下載依賴包到本地cache(默認為$GOPATH/pkg/mod目錄)
go list -m -json all:以 json 的方式打印依賴詳情
其他
go mod graph: 打印項目的模塊依賴結構
go mod tidy :添加缺少的包,且刪除無用的包
go mod verify :校驗模塊是否被篡改過
go mod why: 查看為什么需要依賴
go mod vendor :導出項目所有依賴到vendor下
go mod edit :編輯go.mod文件
4、總結
GoPath所引出的問題,就是因為第三方類庫的包所導致的,所以在有了GoModule之后,GoPath和GoModule就分別負責不同的職責,共同為Golang項目服務
GoPath用來存放我們從網上拉取的第三方依賴包
GoModule用來存放我們自己的Golang項目文件,當自己的項目需要依賴第三方的包的時候,我們通過GoModule目錄下的一個go.mod文件來引用GoPath目錄pkg包下的mod文件夾下的第三方依賴即可
這樣以來,既解決了原來只能局限在GoPath目錄src包下進行編程的問題,也解決了第三方依賴包難以管理和重復依賴占用磁盤空間的問題
