在Go语言编程中,我们经常会接触到依赖管理。简单来讲,这是对项目所需外部包的管理,包括确定所需外部包及其正确版本,并确保在构建和测试项目时其存在。
基础依赖管理则涉及到添加、更新和删除依赖包,锁定依赖包版本等工作。尽管看似琐碎,但这些工作对项目开发至关重要。
但实际上,go的依赖管理让人一言难尽,甚至可以说乱成一团。官方推出了好几种管理机制,比如gopath、vender、dep等,但都用起来不是那么顺手。到了Go 1.11版本,推出了go mod,虽然稍微好些,但和maven比起来,还是差了好几条街。
更让人费解的是,Go居然没有像maven、pip那样的官方依赖包站点。第三方包可以在任意开源站点发布,这就导致了一堆问题,比如下载地址、版本管理、包说明文档等。
由于没有统一的下载地址,Go的解决方案是把包名就当做包的下载地址。虽然很多包都发布在github.com,但也有很多包用其他站点发布,比如`k8s.io、bazil.org、gobot.io`等等。这就导致无法建立加速镜像站点,国内的加速都只能通过代理的方式。
包名的格式也有限制,只要是会被其他项目用到的包,名字就得是下载地址。就算是公司内部的私有包也不能例外,否则就得人工维护依赖。
包的文档格式和API说明也没有标准,直到go官方推出了`pkg.go.dev`站点,才统一了文档风格和包检索。但奇怪的是,这个站点只提供包信息和展示,包还是得在各自的站点发布。
吐槽归吐槽,要用的时候我们还是得用go语言,下面我会给大家讲一讲go的那些管理机制。
## Gopath
GOPATH是Go的一个重要环境变量,被称为工作目录。在Go 1.5版本之前,通过执行`go get ${packageName}`命令,我们可以下载依赖包。这个命令总是下载最新版本的依赖包,并且这些依赖包都会被存放在`${GOPATH}/src`目录下,但是并没有版本控制。
当我们使用import命令导入依赖包时,会按照以下路径查找:`${GOROOT}/src -> ${GOPATH}/src`。其中,`${GOROOT}/src`是Go SDK的安装目录,src文件夹存放了Go的标准库。而`${GOPATH}/src`则是Go的工作目录,src文件夹存放了所有的Go项目。
需要注意的是,当我们需要导入项目内部模块时,也需要从这些固定路径计算,因此所有项目都必须放在工作目录下,否则会出现找不到依赖的问题。这与其他语言有很大的区别,例如Java和Python都支持从项目路径计算,这意味着项目可以放在任意目录。
例如,如果我们的项目名称是demo,那么导入依赖的方式如下:
```
import "fmt" // 导入标准库包
import "demo/utils" // 导入项目内的工具模块包, 注意也从绝对路径查找
import "github.com/forgoer/openssl" // 导入外部第三方包
```
fmt是Go语言的标准库,它的源代码位于`GOROOT/src`目录下。其他非标准库的包,例如你自己编写的包或者第三方包,都应该放在`GOPATH/src`目录下。这是Go语言的包管理机制决定的,它帮助Go语言在编译时找到所需要的包。
如果你想修改fmt包的代码,你可以直接在`GOROOT/src`目录下找到fmt包的源代码进行修改。
但是,我不推荐你这样做,因为这样会改变Go语言的标准行为。
如果你只是想使用修改过的fmt包,你可以在GOPATH/src目录下创建一个新的包,然后在新包中导入fmt包,并在新包中添加或修改你需要的功能。这样,你的代码就可以使用修改过的fmt包,而不会影响到其他使用标准fmt包的代码。
## Vender
Go 1.5版本引入了一个新的特性,叫做vendor机制。这个机制允许每个项目在其目录下有一个名为vendor的文件夹,用来存放该项目所依赖的包。在执行`go build`命令时,Go语言会首先在vendor目录下查找依赖,如果没有找到,才会去GOPATH目录下查找。依赖查找的顺序是:`${GOROOT}/src -> ${Project}/vendor -> ${GOPATH}/src`。
vendor机制的出现主要是**为了解决依赖版本管理**的问题。
它的基本思想是,每个项目的依赖都可以在自己的目录下独立管理,这样就避免了不同项目之间的依赖冲突。但是,这个机制并没有解决依赖包的版本问题,只是将依赖包放在了独立的目录下。如果你想要固定依赖包的版本,你需要将整个vendor目录一起提交到代码仓库。
这种做法虽然在一定程度上解决了版本管理的问题,但是也带来了一系列新的问题。
**首先**,vendor目录通常会非常大,提交到代码仓库会占用大量的空间,并且会降低仓库的下载和更新速度。
**其次**,大量的依赖代码会干扰代码审查,对代码统计和其他性能工具也会产生影响。
**最后**,对于多人协作的项目来说,管理项目的依赖包,记录版本,获取和存放依赖包等工作会变得更加复杂。从某种意义上说,蛮鸡肋的。
## Go mod
它是从Go1.11版本开始引入的模块支持(module)功能的一部分,比起其他依赖管理,挺实用的。GoMod提供了对依赖包的自动获取、版本控制、依赖图分析等功能。
每个项目都会新增一个`go.mod`文件,用于维护该项目的依赖包信息,包括依赖包的版本号。可以使用`go mod init ${ProjectName}`命令来创建这个文件,而文件的内容则由Go自动维护。每次下载或升级依赖时,Go都会自动更新这个文件。
要使用go mod,需要先通过环境变量`GO111MODULE`来启用这个功能。"off"意味着关闭go mod,Go将继续从vendor目录或GOPATH中查找依赖。"on"则表示开启go mod,这时项目必须包含`go.mod`文件。如果没有设置环境变量,或者设置为"auto"(这是Go 1.13及以后版本的默认模式),那么Go会自动判断是否启用go mod:如果项目不在`GOPATH/src`目录下,并且包含`go.mod`文件,那么Go就会启用go mod。
使用GoMod的好处包括:
1. 简化依赖管理:你不再需要将所有的依赖复制到你的项目目录下,GoMod会自动为你下载和管理这些依赖。
2. 便于协作:当其他开发者克隆你的项目时,他们不需要手动安装所有的依赖,只需要运行`go mod download`命令即可。
3. 便于版本控制:你可以在`go.mod`文件中明确指定依赖的版本,这样就可以避免因为依赖包的更新导致的不兼容问题。
4. 提供依赖图分析:通过`go mod graph`命令,你可以查看项目的依赖图,了解各个依赖之间的关系。
当然go mod也有其他的操作:
go mod download :手动触发下载依赖包到本地 cache
go mod graph :打印项目的模块依赖结构
go mod edit :编辑 go.mod 文件
go mod verify:校验模块是否被篡改过
go mod why: 查看为什么需要依赖
go mod vendor:导出项目所有依赖到 vendor 下
总的来说,GoMod是一个强大且易用的依赖管理工具,它可以极大地提高Go语言开发的效率和质量。
## 关于Masutaa
Masutaa是个互联网从业者自由协作交流平台,链接行业内TOP10%人才!目前平台上已经有将近400名互联网尖端人才,其中近70%的从业者从业年限超3年。加入Masutaa,加入自由生活!感兴趣的朋友可以搜索masutaa官网!
有疑问加站长微信联系(非本文作者))