包与依赖管理

Go语言中支持模块化的开发理念,在Go语言中使用包(package)来支持代码模块化和代码复用。一个包是由一个或多个Go源码文件(.go结尾的文件)组成,是一种高级的代码复用方案,Go语言为我们提供了很多内置包,例如我们之前频繁使用的fmt包等。

包 package

定义包

一个包可以简单的理解为一个存放.go文件的文件夹,其中该文件夹内所有.go文件都要在非注释的第一行添加声明,声明该文件归属的包

1
package 包名

**注意:**一个文件夹内直接包含的文件只能归属于一个包,同一个包的文件不能在多个文件夹之下。

包名为main的包是go语言程序的入口包,编译后会得到一个可执行文件,非包含main包源代码编译则不会的到可执行文件。

包的引入

通过import关键字可以引入另外一个包的内容

1
import importname "path/to/package"

其中

  • importname:命名引入的包在此处的名字,通常都省略。默认值为引入包的包名
  • path/to/package:引入包的路径名称,必须使用双引号包裹起来
  • Go语言中禁止循环导入包

一般而言,import语句会放在位于package语句下方。

如果存在引入的多个包中存在相同的包名或是想为引入的包设置一个新包名,则需要用到通过importname指定一个名称

1
2
3
4
5
import f "fmt"

func main() {
f.Printf("Hello World\n")
}

init初始化函数

在每一个go源文件中,都可以添加如下格式的特殊函数

1
2
3
func init(){
// ...
}

这种特殊的函数,不接收任何参数也没有任何返回值,也不可以在代码中主动调用它。当程序启动的时候,init函数会按照package先引入后调用init函数顺序执行

包初始化函数执行顺序示意图

每一个包的初始化是先从初始化包级别变量开始的,先初始化变量再执行init初始化函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

var x int8 = 10

const pi = 3.14

func init() {
fmt.Println("x:", x)
fmt.Println("pi:", pi)
sayHi()
}

func sayHi() {
fmt.Println("Hello World!")
}

func main() {
fmt.Println("你好,世界!")
}
/*
x: 10
pi: 3.14
Hello World!
你好,世界!
*/

匿名引入

如果引入一个包的时候为其设置了一个特殊_作为包名,那么这个包的引入方式就称为匿名引入。

被匿名引入的包中的init函数将被执行并且仅执行一遍。一个包被引入的目的主要是为了加载这个包,且使得这个包的资源得以初始化。

go module

Go module 是 Go1.11 版本发布的依赖管理方案,从 Go1.14 版本开始推荐在生产环境使用,于Go1.16版本默认开启。

由于内容比较长,具体可以查看下面网站了解:

https://www.liwenzhou.com/posts/Go/package/

下载依赖包方式

  • go get手动下载依赖的包,@指定版本。

    下载完成后将会自动记录在go.mod文件中

  • 编辑go.mod文件,使用go mod download下载依赖包

go.mod文件

go.mod文件中记录了当前项目中所有依赖包的相关信息。

声明依赖的格式如下

1
require module/path v1.2.3

其中:

  • module/path:依赖包的引入路径,一般为一个网址,可以通过replace关键词重导向至本地包

    1
    replace module/path => localPath
  • v1.2.3:版本号,有几种格式

    • latest:最新版本
    • v1.2.3:详细版本号
    • commit hash:指定某次commit hash

go.sum文件

这个文件中详细记录了当前项目中引入的依赖包的信息及其hash值。

1
2
<module> <version> <hash>
<module> <version>/go.mod <hash>

由于go语言没有中心化分发的包管理机制,因此通过这种方式对依赖包进行校验。

依赖保存位置

Go module 会把下载到本地的依赖包会保存在 $GOPATH/pkg/mod目录下,每个依赖包都会带有版本号进行区分,这样就允许在本地存在同一个包的多个不同版本。

go clean -modcache 命令可以清除所有本地已缓存的依赖包数据。

使用go module发布包

本节内容具体也请观看上述文章的内容。

  • 将位于云端的项目clone到本地

  • 通过go mod init 仓库地址初始化go项目,注意为github.com/xxx/xxx的样式

  • 使用git tag -a 版本号 -m "描述"为发布的包打上标签,命名版本号

    版本号格式:v主版本号.次版本号.修订号

  • 最后使用push到github上的远程分支

这样,其他的开发者可以通过我们的仓库地址下载并引用这个包了。

如果某个发布的版本存在致命缺陷不再想让用户使用时,我们可以使用retract声明废弃的版本。在go.mod中通过这个关键词可对外声明废弃该版本。

1
2
3
4
5
module github.com/xxx/xxx

go 1.16

retract v0.1.1

Git的使用可以参考我的文章:

https://www.icewindy.cn/2022/04/19/Git入门笔记(1):基本知识与基本操作/