首页 > Go学习 > Go语言中的依赖(包)管理
2021
09-05

Go语言中的依赖(包)管理

在以前,Go语言的的包依赖管理一直都被大家所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的GOPATH到GO VENDOR,虽然走了不少的弯路,但最终还是拿出了Go Modules这样的解决方案

GOPATH模式

通过配置GOPATH环境变量来设置一个工作目录,该目录中包含所有的源码和编译生成的文件

该目录下有三个主要目录,功能各不相同

bin:存放编译后生成的二进制可执行文件
pkg:存放编译后生成的 .a 文件
src:存放项目的源代码,将自己的代码和下载的包全部放在该目录下进行管理

GOPATH模式的问题

无法在项目中,使用指定版本的包
其他人运行你的程序时,无法保证对方下载的包的版本是你所期望的版本,可能无法正常运行
由于所有项目都共享一个GOPATH,需要导入依赖的时候,都来这里找,一个包只能保留一个版本,无法满足多个项目对不同包版本的需求

GO VENDOR模式

为了解决GOPATH模式包的版本问题,Go 1.5开始支持GO VENDOR模式

在每个项目下都创建一个vendor目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。

在编译时,其搜索包的优先级顺序,由高到低是这样的

当前包下的vendor目录
向上级目录查找,直到找到src下的vendor目录
在GOROOT目录下查找
在GOPATH下面查找依赖包

虽然这个方案解决了一些问题,但是仍然存在一些问题

如果多个项目用到了同一个包的同一个版本,这个包会同时存在于不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理
如果想要分享开源项目,需要将所有的依赖包悉数上传,别人使用的时候,除了项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如预期那样正常运行

Go Modules模式

为了解决上面的问题,在v1.11时官方加入了Go Modules模式,在v1.14版本中,官方正式推荐该模式,称其已经足够成熟,可以应用于生产上

为了引入Go Modules模式,go env多个个环境变量:GO111MODULE

这里的111,其实就是v1.11版本的象征标志,如当初vendor出现的时候,也多了个GO15VENDOREXPERIMENT环境变量,其中15表示的v1.5版本

GO111MODULE是一个开关,通过它可以开启或关闭go mod模式

它有三个可选值:off、on、auto,默认是auto

off  表示禁用go mod模式,编译时会从GOPATH和vendor文件夹中查找包
on   表示启用go mod模式,编译时会忽略GOPATH和vendor文件夹,只根据go.mod下载依赖
auto 表示当项目根目录有go.mod文件时,自动开启模块支持

go.mod文件

除了go.mod之外,go还维护一个名为go.sum的文件,其中包含特定模块版本内容的预期加密哈希

go命令使用go.sum文件确保这些模块的未来下载检索与第一次下载相同的位,以确保项目所依赖的模块不会出现意外更改,go.sum不需要手工维护,所以可以不用太关注

go.mod文件的内容格式

module语句:指定模块的名称
go语句:指定项目使用的go版本
require语句:指定的依赖项模块
    如:require golang.org/x/text v0.3.0
    最后的v0.3.0表示指定包的版本
    如果不指定版本或者使用latest,则会自动下载最新版版
    如果指定某个分支名,会自动下载该分支的最新版本
replace语句:替换依赖模块
    如:replace golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
    表示使用后面的包替换前面的包
    在程序中我们仍然要写前面的包
exclude语句:忽略依赖模块
    如:exclude example.com/banana v1.2.4
    表示忽略指定版本的包

go添加了mod命令来配置和操作go mod模式下的包依赖

go mod command

其中可用的command有

download    下载模块到本地缓存,缓存路径是$GOMODCACHE
edit        是提供了命令版编辑go.mod的功能,如go mod edit -fmt go.mod会格式化go.mod
graph       把模块之间的依赖结构打印出来
init pack_name:初始化模块,生成go.mod文件
tidy        增加缺失的包,移除没用的包
vendor      把依赖拷贝到 vendor/ 目录下
verify      校验模块是否被篡改过


edit命令:
go mod edit [-fmt|-print|-json] [go.mod]
最后的go.mod可以省略
-fmt:格式化go.mod文件的内容
-print:打印go.mod文件的原始内容
-json:以json格式打印go.mod文件的内容

go mod edit [editing flags] [go.mod]
最后的go.mod可以省略
editing flags有:
    -require=path[@version] :添加依赖
    -droprequire==path[@version] :删除依赖
    -replace=old[@v]=new[@v] :替换依赖
    -exclude=path@version :添加忽略的依赖

使用实例

切换到项目的根目录中使用命令go mod init pack_name即可初始化模块

在当前目录创建go.mod文件,之后的包的管理都是通过这个文件维护管理

init初始化时指定的模块名称作为引入自己的包名(用来引入项目根目录下的其他目录)

如/path/study目录为我们的项目根目录,在该目中又有一个sql目录(主要存放数据库操作)
/path/study/sql目录中的程序文件声明的包名为mysql

使用命令初始化:cd /path/study & go mod init gostudy

程序文件的内容

package main

import (
    "fmt"
    "gostudy/sql"
)

func main(){
    mysql.Run("select * from info")
    fmt.Println("执行了sql目录中的Run方法")
}

从以上代码我们可以看出来

    1、初始化时声明的包名和程序中声明的包无关,也可目录名无关
    2、初始化时声明的包仅仅是用来在导入包时作为根目录

如何添加依赖

1、只要在项目中有import,当使用go run、go build或者go test等命令就会自动下载(如果发现本地没有)并把具体的依赖关系和版本写入到go.mod和go.sum文件中

2、使用命令go get -v -u path@version下载后,会自动写入go.mod
    go get命令的选项
    -d :表示仅下载依赖但不安装依赖
    -u :表示使用网络更新下载依赖,默认情况下不检测依赖的更新
    -fix:表示在解析依赖项或生成代码之前对下载的包运行修复工具
    -t :表示同时下载为指定包构建测试所需的包
    -f :必须同时使用-u才有效,表示当本地存在时不验证本地包是否是从指定网址下载,如果修改了包的源文件可以使用该选项
    -v :启用详细进度和调试输出
最后编辑:
作者:qingheluo
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。