大家好。我是明哥在学习Golang的那段时间,我写了详细的学习笔记,放在了我的个人微信上公众号《Go编程时光》。我也是Go语言初学者,所以我写的应该更适合刚接触的同学。如果你也是Go语言新手,不妨关注一下,一起学习成长。在线博客:http://golang.iswbm.comGithub:github.com/iswbm/GolangCodingTime过去Go语言的包依赖管理一直被大家诟病,Go官方一直在努力为开发者提供更方便的简单易用的包管理方案,从最初的GOPATH到GOVENDOR,再到最新的GOModules,虽然走了很多弯路,但最终还是想出了GoModules这样一个像样的解决方案。目前最主流的包依赖管理方式是使用官方推荐的GoModules。Go1.14版本于前段时间发布。官方正式宣布强烈推荐大家使用GoModules,有信心在生产中使用。本文将对GoModules的使用进行长篇大论的讲解,但在此之前,我还是简单介绍一下前两个解决方案,GOPATH和govendor。我觉得这是很有必要的,因为只有了解它的发展历程,才能知道GoModules的到来有多么的艰难和意义。一、最古老的GOPATHGOPATH应该很多人都不陌生。之前配置环境的时候配置过吗?你可以理解为工作目录。在这个工作目录下,通常有如下目录结构。每个目录存放的文件是不同的。bin:存放编译后生成的二进制可执行文件pkg:存放编译后的.afilesrc:存放项目的源代码,可以是你自己写的代码,也可以是你用goget下载的包。将你所有的包或者其他人的包放在$GOPATH/src目录下进行管理。我们称之为GOPATH模式。在这种模式下,使用goinstall时,生成的可执行文件会放在$GOPATH/bin中。如果你安装了一个库,.a文件会在$GOPATH/pkg下相应的平台目录下生成(通过GOOS和GOARCH),生成一个后缀为.a的文件。GOOS表示目标操作系统,包括darwin(Mac)、linux、windows、android、netbsd、openbsd、solaris、plan9等。GOARCH表示目标架构,常见的有arm、amd64等。这两个都是goenv可以在goenv变量名中查看变量。到目前为止,你可能不会认为上述解决方案会造成任何问题,直到你开始真正使用GOPATH开发程序,你将不得不面对各种问题,其中最严重的是版本管理问题,因为GOPATH没有完全没有版本的概念。以下几点是你在使用GOPATH时肯定会遇到的问题:不能在你的项目中使用指定版本的包,因为不同版本包的导入方式是一样的。当其他人运行你开发的程序时,他们无法确定他下载的包的版本就是你期望的版本。当对方使用其他版本时,可能会导致程序无法在本地正常运行。一个包只能保留一个版本,也就是说你在本地开发的所有项目都必须用同一个版本的包,这几乎是不可能的。2、govendor模式的过渡为了解决GOPATH方案下不同项目下无法使用多个版本库的问题,Gov1.5开始支持vendor。过去使用GOPATH时,所有项目共享同一个GOPATH。当他们需要导入依赖时,他们都来这里找。俗话说,GOPATH模式下第三方库只能有一个版本。解决方法是在每个项目下创建一个vendor目录,每个项目需要的依赖只会下载到自己的vendor目录下,项目之间的依赖不会相互影响。编译时,v1.5的Go设置启用GO15VENDOREXPERIMENT=1(注:此变量在v1.6中默认为1,但在v1.7之后,此环境变量已被移除,默认启用vendor特性,不需要手动设置后),vendor目录的依赖包搜索路径的优先级会提高(相对于GOPATH)。搜索包的优先级顺序,从高到低依次为:当前包下的vendor目录,向上一级目录搜索,直到找到src下的vendor目录,GOROOT目录下搜索,搜索依赖包在GOPATH下。这种解决方案虽然解决了一些问题,但并不完美。如果多个项目使用同一个包的同一个版本,包将存在于机器上的不同目录中,不仅浪费磁盘空间,而且无法对第三方包进行集中管理(分散在各个角落).而如果你想分享你的开源项目,你需要上传你所有的依赖包。别人使用的时候,除了你的项目源码外,所有的依赖包都要下载,以保证别人使用的时候,不会因为版本问题导致项目不能正常运行。这些看似没什么问题的问题,会让我们的开发和使用过程非常不爽。虽然我是初学者,没有用过govendor,但我能有一个明显的预感,这个方案还是会让我崩溃的。3、gomod的诞生Gomodulesv1.11版本正式上线。在最新的v1.14版本中,官方发表声明称已经足够成熟,可以用于生产。从v1.11开始,goenv增加了一个环境变量:GO111MODULE,这里的111其实就是v1.11的标志。Go似乎很喜欢这种命名方式。比如vendor出现时,多了一个GO15VENDOREXPERIMENT环境变量,其中15表示vendor诞生于v1.5。GO111MODULE是一个开关,通过它可以打开或关闭gomod模式。它有三个可选值:off、on、auto,默认值为auto。GO111MODULE=off禁用模块支持,编译时将从GOPATH和供应商文件夹中查找包。GO111MODULE=on启用模块支持,GOPATH和vendor文件夹在编译时会被忽略,依赖只会根据go.mod下载。GO111MODULE=auto,当项目在$GOPATH/src之外,且项目根目录下有go.mod文件时,自动开启模块支持。gomod出来后,GOPATH(肯定没人用)和GOVENDOR会被淘汰,但是如果你的项目还在用那些即将过时的包依赖管理方案,请注意设置GO111MODULE为离开。如何设置?您可以使用goenv命令。如果我想启用gomod,我使用这个命令$goenv-wGO111MODULE="on"4.Gomod依赖管理接下来,我们来演示一下gomodules是如何管理包依赖的。gomod不再依赖$GOPATH,这样就可以在没有GOPATH的情况下创建项目,所以我们在home目录下创建一个go_test目录来创建我的项目。具体操作如下:接下来进入项目目录,执行以下命令接下来重点是gomodules的初始化。我们需要通过goinstall查看下载的包安装在哪里?上面我们观察到,使用gomodules模式后,项目目录下会多生成两个文件,分别是go.mod和go.sum。这两个文件是gomodules的核心,这里不得不介绍一下。go.modgo.mod文件的内容比较容易理解。第一行:模块的引用路径。第二行:项目使用的go版本。第三行:项目需要的直接依赖包及其版本。在实际应用中,你会看到比较复杂的go.mod文件,比如下面的模块github.com/BingmingWong/module-testgo1.14require(example.com/applev0.1.2example.com/bananav1.2.3example.com/banana/v2v2.3.4example.com/pear//间接example.com/strawberry//不兼容)excludeexample.com/bananav1.2.4replace(golang.org/x/cryptov0.0.0-20180820150726-614d502a4dac=>github.com/golang/cryptov0.0.0-20180820150726-614d502a4dacgolang.org/x/netv0.0.0-20180821023952-922f4815f713=>github.com/golang/netv0.0.0-20180826012351-8texta4630e=>github4630ecom/golang/textv0.3.0)主要是因为多了两个flags:exclude:忽略指定版本的依赖包replace:因为golang.org/x在国内的每个包都需要绕墙,可以使用replace在go.mod中将其替换为github上的相应库。go.sum文件与go.sum文件相比,它更加复杂和密集。可以看出,虽然内容很多,但不难理解,每一行都是由模块路径、模块版本、hash校验值组成。哈希校验值用于保证当前缓存的模块不会被篡改。hash是以h1:开头的字符串,表示生成校验和的算法是第一版哈希算法(sha256)。值得注意的是,为什么有的包只有一行
