没有理想的人不伤心

Golang - 包和依赖管理

2025/03/03
1
0

包管理

每个包一般都定义了一个不同的名字空间用于它内部的每个标识符的访问。每个名字空间关联到一个特定的包

每个包还通过控制包内名字的可见性和是否导出来实现封装特性。通过限制包成员的可见性并隐藏包 API 的具体实现,将允许包的维护者在不影响外部包用户的前提下调整包的内部实现。通过限制包内变量的可见性,还可以强制用户通过某些特定函数来访问和更新内部变量,这样可以保证内部变量的一致性和并发时的互斥约束。

每个包是由一个全局唯一的字符串所标识的导入路径定位,为了避免冲突,所有非标准库包的导入路径建议以所在组织的互联网域名为前缀

包声明

在每个 Go 语言源文件的开头都必须有包声明语句。包声明语句的主要目的是确定当前包被其它包导入时默认的标识符(也称为包名)。

package main

通常来说,默认的包名就是包导入路径名的最后一段,因此即使两个包的导入路径不同,它们依然可能有一个相同的包名。例如,math/rand 包和 crypto/rand 包的包名都是 rand

关于默认包名一般采用导入路径名的最后一段的约定也有三种例外情况。第一个例外,包对应一个可执行程序,也就是 main 包,这时候 main 包本身的导入路径是无关紧要的。名字为 main 的包是给 go build(§10.7.3)构建命令一个信息,这个包编译完之后必须调用连接器生成一个可执行程序。

第二个例外,包所在的目录中可能有一些文件名是以_test.go 为后缀的 Go 源文件(译注:前面必须有其它的字符,因为以_或.开头的源文件会被构建工具忽略),并且这些源文件声明的包名也是以_test 为后缀名的。这种目录可以包含两种包:一种是普通包,另一种则是测试的外部扩展包。所有以_test 为后缀包名的测试外部扩展包都由 go test 命令独立编译,普通包和测试的外部扩展包是相互独立的。测试的外部扩展包一般用来避免测试代码中的循环导入依赖,具体细节我们将在 11.2.4 节中介绍。

第三个例外,一些依赖版本号的管理工具会在导入路径后追加版本号信息,例如“gopkg.in/yaml.v2”。这种情况下包的名字并不包含版本号后缀,而是 yaml。

如果我们想同时导入两个有着名字相同的包,例如 math/rand 包和 crypto/rand 包,那么导入声明必须至少为一个同名包指定一个新的包名以避免冲突。这叫做导入包的重命名。

import(
    "crypto/rand"
    mrand"math/rand" // alternative name mrand avoids conflict
)

导入包的重命名只影响当前的源文件。其它的源文件如果导入了相同的包,可以用导入包原本默认的名字或重命名为另一个完全不同的名字。

每个导入声明语句都明确指定了当前包和被导入包之间的依赖关系。如果遇到包循环导入的情况,Go 语言的构建工具将报告错误。

包的匿名导入

如果只是导入一个包而并不使用导入的包将会导致一个编译错误。但是有时候我们只是想利用导入包而产生的副作用:它会计算包级变量的初始化表达式和执行导入包的 init 初始化函数。这时候我们需要抑制“unused import”编译错误,我们可以用下划线_来重命名导入的包。像往常一样,下划线_为空白标识符,并不能被访问。

import _ "image/png" // register PNG decoder

依赖管理

演变: gopath --> go vendor --> go module

  1. gopath

1730610698372-78d913a8-2ae7-40af-852a-38ee4f93bc8e.png

局限性:如当两个本地项目 A 和 B 依赖于同一个包的不同版本,在这两个版本之间并没有完全向下兼容(有些函数删掉了),由于项目依赖于 src 下的包,所以这两个项目不能同时编译成功

也就是无法实现多版本的 package 控制

  1. go vendor

1730610936924-01b83150-e0fa-44e3-aa3d-8e33346b3dee.png

也就是在 A 和 B 的 vendor 目录下分别使用不同版本的包,以此解决多版本控制问题

局限性:当 A 项目的依赖包 B、和 C 同时依赖于同一个包 D 的不同版本,那么编译时会产生错误

也就是无法控制依赖的版本

  1. go module

1730611208338-61adac16-3121-49d3-b7af-5200fb6229b7.png

依赖管理三要素

  1. 配置文件,描述依赖关系
    ==> go.mod 文件
  2. 中心仓库,管理依赖库
    ==> goproxy
  3. 本地工具,获取依赖库、自动生成依赖关系配置文件
    ==> go get/ go mod

go.mod 文件的组成

1730611814966-68644269-db03-45ec-af97-0341f8ae2796.png

关键字:

indirect:间接依赖,也就是 A->B、B->C,那么 A->C 就是间接依赖,使用 indirect 标识

+incompatible:由于 gomod 是在 go1.1 之后才实验性引入,有一些包并没有 go.mod 文件,对于没有 go.mod 文件且主版本即 V2+的包,会加上该关键字,标识也许会产生不兼容等问题

go 对于版本号 version 的定义:

1730612025543-20112a88-9724-4ce5-9e1a-c434095d57ec.png

go get 用法

1730614611844-a7fe5ade-8114-4ad3-ab37-c977f412e15c.png

go mod 用法

1730614628732-8fff225e-ab5e-4c0f-8136-8d929aaeefa8.png