import的基本用法
1. 导入包的3种方法
Go中导入包有3种方法:
- 标准库
- 相对路径
- 绝对路径(相对于$GOPATH/src的路径)
例如:
1 | import "fmt" // 标准库,去GOROOT下加载该模块 |
执行import时会去寻找$GOPATH
下的src
文件夹,src
文件夹是默认的源代码所在的文件夹。使用GoLand IDE时,如果没有src
文件夹,它会自动生成一个,在导入的路径下还会生成一个.idea
文件夹。
2. vendor文件夹
2.1. 提出问题
我们知道,一个工程稍大一点,通常会依赖各种各样的包。而Go使用统一的GOPATH管理依赖包,且每个包仅保留一个版本。而不同的依赖包由各自的版本工具独立管理,所以当所依赖的包在新版本发生接口变更或删除时,会面临很多问题。
为避免此类问题,我们可能会为不同的工程设置不同的GOPATH,或者更改依赖包路径名称。这样手动维护起来也很头疼。
2.2. 解决问题
Go 1.5引入了vendor文件夹,其对语言使用,go命令没有任何影响。若某个路径下边包含vendor文件夹,则在某处引用包时,会优先搜索vendor文件夹下的包。vendor文件夹必须要在$GOPATH/src
下。
在Go 1.5开启该项特性需设置GO15VENDOREXPERIMENT=1
,而从Go 1.6开始,该项特性默认开启。
2.3. 使用方式
2.3.1 vendor搜索方式
vendor包的搜索方式为:自包引用处,从其所在文件夹查询是否有vendor文件夹包含所引用包;若没有,然后从其所在文件夹的上层文件夹寻找是否有vendor文件夹包含所引用包,若没有,则再搜索上层文件夹的上层文件夹…,直至搜索至$GOPATH/src
并搜索完成时止。
例如,如下代码中,$GOPATH/src/x/y/z/main.go
引用了包”v”,则不论vendor/v/v.go
置于
- src/
- src/x/
- src/x/y/
- src/x/y/z/
中任意一个文件夹下,均可以找到。
1 | $ cat $GOPATH/src/x/y/z/main.go |
1 | $ cat vendor/v/v.go |
1 | $ go run main.go |
当vendor存在嵌套时,若不同的vendor文件夹包含相同的包,且该包在某处被引用,寻找策略仍遵循如上规则。即从包引用处起,逐层向上层文件夹搜索,首先找到的包即为所引,也就是从$GOPATH/src
来看,哪个vendor包的路径最长,使用哪个。
如下代码中,$GOPATH/src/x/y/z/main.go所在工程有两个vendor文件夹,分别位于:
$GOPATH/src/x/vendor/v/
$GOPATH/src/x/y/z/vendor/v/
包含相同的包”v”,目录树为:
1 | $ tree $GOPATH/src |
1 | $ cat $GOPATH/src/x/vendor/v/v.go |
1 | $ cat $GOPATH/src/x/y/z/vendor/v/v.go |
输出为:
1 | $ go run main.go |
可以看到,真正调用的是$GOPATH/src/x/y/z/vendor/v/v.go
。
2.3.2 vendor使用规约
使用vendor时,建议遵循如下两条规约。
- 当欲将某包vendor时,可能想将所有依赖包均vendor;
- 尽量将vendor依赖包结构扁平化,不要vendor套vendor。
如下示例代码演示vendor扁平化使用。main.go
位于$GOPATH/src/github.com/olzhy/test
下。
1 | package main |
$GOPATH/src/github.com/olzhy/test
目录树:
1 | ├─ main.go |
3. 寻找导入的package的方法
在导入了一个工程,比如github上的repo之后,寻找工程中import的包的方法:
- GO111MODULE=off 禁用 go module,编译时会从 GOPATH 和 vendor 文件夹中查找包;
- GO111MODULE=on 启用 go module,编译时会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod下载依赖;
- GO111MODULE=auto(默认值),当项目在 GOPATH/src 目录之外,并且项目根目录有 go.mod 文件时,开启 go module。
关于Go Module的介绍: Go Module
在没有开启Go Module的时候,寻找依赖包的顺序:
- 寻找本地的vendor路径(如果有的话);
- 如果不能在vendor路径下找到被导入的包,到当前路径的上一级路径寻找vendor路径(如果有的话),看是否能找到被导入的包;
- 继续第2步,直到到达
$GOPATH/src
; - 如果还没有找到被导入的包,查看
$GOROOT
; - 如果
$GOROOT
下也没有,在$GOPATH/src
路径下寻找。