go mod包管理工具

最早的时候，Go 语言所依赖的所有的第三方库都放在 GOPATH 这个目录下面，这就导致了同一个库只能保存一个版本的代码。如果不同的项目依赖同一个第三方的库的不同版本，应该怎么解决？
go module 是 Go 语言从 1.11 版本之后官方推出的版本管理工具，并且从 Go1.13 版本开始，go module 成为了 Go 语言默认的依赖管理工具。

常用的go mod命令如下表所示：

命令 | 作用
go mod download | 下载依赖包到本地 (默认为 GOPATH/pkg/mod 目录)
go mod edit | 编辑 go.mod 文件
go mod graph | 打印模块依赖图
go mod init | 初始化当前文件夹，创建 go.mod 文件
go mod tidy | 增加缺少的包，删除无用的包
go mod vendor | 将依赖复制到 vendor 目录下
go mod verify | 依赖校验
go mod why | 解释为什么需要依赖

GOPROXY

proxy 顾名思义就是代理服务器的意思。大家都知道，国内的网络有防火墙的存在，这导致有些 Go 语言的第三方包我们无法直接通过go get命令获取。GOPROXY 是 Go 语言官方提供的一种通过中间代理商来为用户提供包下载服务的方式。要使用 GOPROXY 只需要设置环境变量 GOPROXY 即可。
目前公开的代理服务器的地址有：

goproxy.io；

goproxy.cn：（推荐）由国内的七牛云提供。
MacOS 或 Linux 下设置 GOPROXY 的命令为：

Copy
export GOPROXY=https://goproxy.cn

Go 语言在 1.13 版本之后 GOPROXY 默认值为 https://proxy.golang.org 在国内可能会存在下载慢或者无法访问的情况，所以十分建议大家将 GOPROXY 设置为国内的 goproxy.cn。

使用 go get 命令下载指定版本的依赖包

执行go get 命令，在下载依赖包的同时还可以指定依赖包的版本。

运行go get -u命令会将项目中的包升级到最新的次要版本或者修订版本；

运行go get -u=patch命令会将项目中的包升级到最新的修订版本；

运行go get [包名]@[版本号]命令会下载对应包的指定版本或者将对应包升级到指定的版本。
提示：go get [包名]@[版本号]命令中版本号可以是 x.y.z 的形式，例如 go get foo@v1.2.3，也可以是 git 上的分支或 tag，例如 go get foo@master，还可以是 git 提交时的哈希值，例如 go get foo@e3702bed2。

如何在项目中使用

【示例 1】创建一个新项目：

在 GOPATH 目录之外新建一个目录，并使用go mod init初始化生成 go.mod 文件。

go mod init hello
go: creating new go.mod: module hello

go.mod 文件一旦创建后，它的内容将会被 go toolchain 全面掌控，go toolchain 会在各类命令执行时，比如go get、go build、go mod等修改和维护 go.mod 文件。

go.mod 提供了 module、require、replace 和 exclude 四个命令：

module 语句指定包的名字（路径）；

require 语句指定的依赖项模块；

replace 语句可以替换依赖项模块；

exclude 语句可以忽略依赖项模块。

初始化生成的 go.mod 文件如下所示：

module hello

go 1.13

添加依赖。

新建一个 main.go 文件，写入以下代码：

Copy
1. package main

3. import (
4. "net/http"
5. "github.com/labstack/echo"
6. )

8. func main() {
9. e := echo.New()
10. e.GET("/", func(c echo.Context) error {
11. return c.String(http.StatusOK, "Hello, World!")
12. })
13. e.Logger.Fatal(e.Start(":1323"))
14. }

执行go run main.go运行代码会发现 go mod 会自动查找依赖自动下载：

Copy
go run main.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: extracting github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon v0.3.0
......
go: finding golang.org/x/text v0.3.0

____    __
/ __/___/ /  ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:1323

现在查看 go.mod 内容：

Copy
module hello

go 1.13

require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
)

go module 安装 package 的原则是先拉取最新的 release tag，若无 tag 则拉取最新的 commit，详见 Modules 官方介绍。

go 会自动生成一个 go.sum 文件来记录 dependency tree：

github.com/davecgh/go-spew v1.1.0/go.mod h1/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/labstack/echo v3.3.10+incompatible h1=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.3.0 h1=
github.com/labstack/gommon v0.3.0/go.mod h1+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
... 省略很多行

再次执行脚本go run main.go发现跳过了检查并安装依赖的步骤。

可以使用命令go list -m -u all来检查可以升级的 package，使用go get -u need-upgrade-package升级后会将新的依赖版本更新到 go.mod * 也可以使用go get -u升级所有依赖。

【示例 2】改造现有项目。

项目目录结构为：

├─ main.go
│
└─ api
└─ apis.go

main.go 源码为：

Copy
1. package main

3. import (
4. api "./api" // 这里使用的是相对路径
5. "github.com/labstack/echo"
6. )

8. func main() {
9. e := echo.New()
10. e.GET("/", api.HelloWorld)
11. e.Logger.Fatal(e.Start(":1323"))
12. }

api/apis.go 源码为：

Copy
1. package api

3. import (
4. "net/http"

6. "github.com/labstack/echo"
7. )

9. func HelloWorld(c echo.Context) error {
10. return c.JSON(http.StatusOK, "hello world")
11. }

使用 go mod init *** 初始化 go.mod。

go mod init hello
go: creating new go.mod: module hello

运行go run main.go。

Copy
go run main.go
go: finding golang.org/x/crypto latest
build _/D_/code/src/api: cannot find module for path _/D_/code/src/api

首先还是会查找并下载安装依赖，然后运行脚本 main.go，这里会抛出一个错误：

build /D/code/src/api: cannot find module for path /D/code/src/api

但是 go.mod 已经更新：

Copy
module hello

go 1.13

require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
)

那为什么会抛出这个错误呢？

这是因为 main.go 中使用 internal package 的方法跟以前已经不同了，由于 go.mod 会扫描同工作目录下所有 package 并且变更引入方法，必须将 hello 当成路径的前缀，也就是需要写成 import hello/api，以往 GOPATH/dep 模式允许的 import ./api 已经失效。

更新旧的 package import 方式。

所以 main.go 需要改写成：

Copy
1. package main

3. import (
4. api "hello/api" // 这里使用的是相对路径
5. "github.com/labstack/echo"
6. )

8. func main() {
9. e := echo.New()
10. e.GET("/", api.HelloWorld)
11. e.Logger.Fatal(e.Start(":1323"))
12. }

提示：在 Go 语言 1.11 版本下使用 go mod 时可能会遇到 go build github.com/valyala/fasttemplate: module requires go 1.12 这种错误，遇到类似这种需要升级到 1.12 的问题，直接升级到 Go 语言 1.12 版本以上就好了。

到这里就和新创建一个项目没什么区别了。

goimoprts 能做什么

Goimports 可以自动将依赖分成标准库和非标准库两个组并将组内的依赖进行排序。

goimports 有什么缺陷

Goimports 最大的问题是没法处理空行，一旦依赖之间通过空行隔开了，那么 goimports 会把两个空行之间的代码作为一个独立分组，此时 import 代码块内整体上的分组和排序就无法保证了。