5、依赖(包)管理

包(package)

Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如 fmt、os、io 等。

任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是package pacakgeName 语句,通过该语句声明自己所在的包。

包的习惯用法

包的导入

导入语法

//单行导入
import "fmt"
//多行导入
import(
	"fmt"
  "os"
)

导入路径

1、全路径

根目录是GOPATH/src/

import "lab/test"
import "database/sql/driver"
2、相对路径

相对路径只能用于导入GOPATH 下的包,标准包的导入只能使用全路径导入。

相对于当前包所在路径

import "../a"

包的引用

在Golang中,一个包下存在多个源文件,若要在某个源文件中引入其他源文件声明的函数、结构体、全局变量等成员,引用规则如下:

例如有项目结构如下

- controller
	- c1.go
	- c2.go
- main.go
- go.mod

go.mod

module top.ygang/demo

go 1.21.5

c1.go

package controller

var Contro1Variable int

func Contro1() {
  
}

c2.go

package controller

func Contro2() {
  Contro1()
}

main.go

package main

import "top.ygang/demo/controller"

func main() {
  controller.Contro1()
  controller.Contro2()
  controller.Contro1Variable = 10
}

1、标准引用

直接使用包名来引用

import "fmt"
fmt.Println("hello")

2、别名引用

一般用于引用了两个同名包时区分使用

import F "fmt"
F.Println("hello")

3、省略引用

把 fmt 包直接合并到当前程序中,在使用 fmt 包内的方法是可以不用加前缀fmt.,直接引用

import . "fmt"
Println("hello")

4、匿名引用

在引用某个包时,如果只是希望执行包初始化的 init 函数,而不使用包内部的数据时,可以使用匿名引用格式

import _ "fmt"

包加载流程

Go 包的初始化

Go语言包的初始化有如下特点:

封装

封装的好处:

如何体现封装:

封装的实现步骤:

init函数

init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。init()函数通常被用来:

init()函数无参数、无返回值

func init(){
    
}

注意:一个包中可以包含多个init()函数,但是执行顺序无法确认,所以不建议这么做!

包管理

最初的时候Go语言所依赖的所有的第三方包都放在 GOPATH 目录下面,这就导致了同一个包只能保存一个版本的代码

godep(govendor)

注意:该方式现已不推荐使用,原因:放弃了依赖重用,使得冗余度上升。同一个依赖包如果不同工程想重用,都必须各自复制一份在自己的vendor目录下。

godep 是一个Go语言官方提供的通过 vender 模式来管理第三方依赖的工具,类似的还有由社区维护的准官方包管理工具 dep。

Go语言从 1.5 版本开始开始引入 vendor 模式,如果项目目录下有 vendor 目录,那么Go语言编译器会优先使用 vendor 内的包进行编译、测试等。

go module(重点)

go module 是Go语言从 1.11 版本之后官方推出的版本管理工具,项目就不需要再放到GOPATH下了,并且从 Go1.13 版本开始,go module 成为了Go语言默认的依赖管理工具。

GOMODULE模式和GOPATH模式一样都指定了依赖包存放的位置,而不是如vendor模式放入工程内,区别在于GOMODULE的go.mod文件中记录了所依赖包的具体版本,既实现了工程之间重用依赖包,且解决了GOPATH模式下不同工程无法依赖同一个包的不同版本的问题。

GO111MODULE

在Go语言 1.12 版本之前,要启用 go module 工具首先要设置环境变量 GO111MODULE,不过在Go语言 1.13 及以后的版本则不再需要设置环境变量。通过 GO111MODULE 可以开启或关闭 go module 工具。

Windows 下开启 GO111MODULE 的命令为:

set GO111MODULE=on 
#或者 
set GO111MODULE=auto

MacOS 或者 Linux 下开启 GO111MODULE 的命令为:

export GO111MODULE=on 
#或者 
export GO111MODULE=auto

使用 go module 的go mod init projectname 命令后会在当前目录下生成一个go. mod 文件,并且在编译/运行当前目录下代码或者使用go get 命令的时候会在当前目录下生成一个 go.sum 文件。

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

go.mod

go.mod 文件记录了项目所有的依赖信息,其结构大致如下:

module demo

go 1.13

require (
    github.com/astaxie/beego v1.12.0
    github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
)

go.mod 文件内提供了module、require、replace和exclude四个关键字。

go.sum

go.sum 文件则是用来记录每个依赖包的版本及哈希值,如下所示:

github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y=
github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=

常用的 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 解释为什么需要依赖

go get

go get 命令用于从远程代码仓库(比如 Github )上下载并安装代码包。

注意:如果GO111MODULE=on或者auto,在go get下载包时候,会下载到GOPATH/pkg/mod,引入时也是同样的从这个目录开始;如果运行了go env -w GO111MODULE=off,那么在go get下载包时候,会下载到GOPATH/src 目录下

常用命令标记:

-d	# 让命令程序只执行下载动作,而不执行安装动作。
-f	# 仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里 Fork 过来的,那么这样做就尤为重要了。
-fix	# 让命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。
-insecure	# 允许命令程序使用非安全的 scheme(如 HTTP )去下载指定的代码包。如果你用的代码仓库(如公司内部的 Gitlab )没有HTTPS 支持,可以添加此标记。请在确定安全的情况下使用它。
-t	# 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包。
-u	# 让命令利用网络来更新已有代码包及其依赖包。默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。

go get [包名]@[版本号]命令中版本号可以是 x.y.z 的形式,例如 go get foo@v1.2.3,也可以是 git 上的分支或 tag,例如 go get foo@master,还可以是 git 提交时的哈希值,例如 go get foo@e3702bed2

第三方包查询:https://pkg.go.dev/