LOADING

正在加载

Go基础学习(拾)——包

壹 包的介绍

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

贰 包的定义

我们可以根据自己的需要创建自定义包。一个包可以简单理解为一个存放.go文件的文件夹,该文件夹下面的所有.go文件都要在非注释的第一行添加如下声明,声明该文件归属的哪个包。

package 自定义包的名字
// package:声明包的关键字
// packagename:包名,可以不与文件夹的名称一致,但是不能包含 - 符号,所有的包名都应该使用小写字母,最好与其实现的功能相对应。

注意:一个文件夹下存放的所有.go文件只能归属一个包,归属在同一个包的.go文件不能存放在多个文件夹下。包名为main的包是Go语言的应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件,而是。

叁 标识符可见性

在同一个包内部声明的标识符都归属与同一个命名空间,而在不同的包内部声明的标识符就属于各自的命名空间。想要在包的外部使用包内部的标识符就需要添加包名前缀,例如fmt.Println("Hello world!"),就是指调用fmt包中的Println函数。

如果想让一个包中的标识符(如变量、常量、类型、函数等)能被外部的包使用,那么标识符必须是对外可见的,这就像Java的权限修饰符(public)公共权限一样。而在Go语言中是通过标识符的首字母大/小写来控制标识符的对外可见(public)/不可见(private)。在一个包内部只有首字母大写的标识符才是对外可见的,才能被外部使用。

package pkg2
 
import "fmt"
 
// 包变量可见性
 
var a = 100 // 首字母小写,外部包不可见,只能在当前包内使用
 
// 首字母大写外部包可见,可在其他包中使用
const Mode = 1
 
type person struct { // 首字母小写,外部包不可见,只能在当前包内使用
    name string
}
 
// 首字母大写,外部包可见,可在其他包中使用
func Add(x, y int) int {
    return x + y
}
 
func age() { // 首字母小写,外部包不可见,只能在当前包内使用
    var Age = 18 // 函数局部变量无论开头是大小写,由于只在当前函数内部的作用域,外部包不可见,只能在当前函数内使用
    fmt.Println(Age)
}

结构体中的字段名和接口中的方法名如果首字母都是大写,外部包可以访问这些字段和方法。

type Student struct {
    Name  string // 可在包外访问的方法
    class string // 仅限包内访问的字段
}
type Payer interface {
    init() //仅限包内访问的方法
    Pay()  //可在包外访问的方法
}

肆 包的引用

当我们需要在当前包中使用另外一个包的内容时,就需要使用import关键字引入这个包,并且import语句通常放在package声明语句的下方。完整的引入声明语句格式如下:

import importname "path/to/package"
// importname:为引入包取的别名,通常都省略,省略的话,默认值为引入包的包名
// path/to/package:引入包的路径名称,包名是从$GOPATH/src/后开始计算的,,使用/进行路径分隔,必须使用双引号包裹起来
// Go语言中禁止循环导入包

引入包可以使用如下格式:

import "包1"
import "包2"

也可以批量引用:

import (
    "包1"
    "包2"
)

当引入的多个包中存在相同的包名或者想为某个引入的包设置一个新包名时,都需要通过importname指定一个在当前文件中使用的新包名。例如,在引入fmt包时为其指定一个新包名f

import f "fmt"

或者批量:

import (
    f "fmt"
    o "os"
)

使用包里面的函数就可以通过别名进行调用:

f.Println("Hello world!")

如果引入一个包的时候为其设置了一个特殊_作为别名,那么这个包的引入方式就称为匿名引入。一个包被匿名引入的目的主要是为了加载这个包,从而使得这个包中的资源得以初始化。 被匿名引入的包中的init函数(这个函数下面会仔细讲解)将被执行并且仅执行一遍。

// 该包是引用mysql数据库包,需要进行初始化
import _ "github.com/go-sql-driver/mysql"

匿名引入的包与其他方式导入的包一样都会被编译到可执行文件中。需要注意的是,Go语言中不允许引入包却不在代码中使用这个包的内容,如果引入了未使用的包则会触发编译错误,但是使用_设置别名,导入的包是已经加载使用的了,不会报错!
还有一种使用点的操作:

import(
    . "fmt" 
)

这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的:

fmt.Println( "hello go" )

可以写成:

Println( "hello go" )

伍 init初始化函数

Go语言有一个特殊的函数init,会在每个包完成初始化后自动执行,并且执行的优先级比main函数高,实现包级别的一些初始化操作!该函数默认没有执行代码,需要用户自定义初始化!
init函数有一下特性:

  • init函数是程序执行前对包进行初始化的函数
  • 每个包可以有多个init函数,并且是以顺序方式执行
  • 包的每个源文件也可以有多个init函数
  • 同一个包中多个init函数的执行顺序是顺序执行
  • 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
  • init函数不能被其他函数调用,而是在main函数执行之前,自动被调用,如果被其他函数调用会出现undefined:init错误
  • init函数没有参数也没有返回值

init函数的主要作用:

  • 初始化不能采用初始化表达式的初始化变量
  • 需要在main函数运行前的执行的代码,就是对包的初始化
  • 实现sync.Once功能(后面的并发会讲解该功能)

对于单个包的初始化,总是以单线程执行,初始化的顺序是全局声明 => init函数 => main函数

package main

// 1.初始化全局声明
var value int

func init() {
    // 2.初始化init函数内的语句
    print("开始初始化变量value!\n")
    value = 1
}

func main() {
    // 3.运行main函数内的语句
    print("初始化后的变量value:", value)
}

上面是没有引入其他包的情况下,对于引入其他包,应用程序的初始化是最前面的包初始化(进入其他包还是按照单个包初始化顺序进行) => 全局声明 => init函数 => main函数
bb1cbce6376d4be56081e7f41f592a55.png

像前面一样,如果只需要一个包的init初始化函数,不需要这个包的其他方法,可以使用匿名引入,这样就表示只执行这个包的init函数。

import _ "github.com/go-sql-driver/mysql"

这里需要注意无论包被导入多少次,初始化只需要一次。这样的好处是开发者如果引用第三方库的时候,本地的库引用了与第三方库的引用的库重复,就不会多次初始化库,这也使得Go语言允许开发者对外发布包或者库,也支持开发者在自己的代码中引入第三方库。

陆 Go包的依赖管理介绍

最早的时候,Go所依赖的所有的第三方库都放在GOPATH这个目录下面。这就导致了同一个库只能保存一个版本的代码。如果不同的项目依赖同一个第三方的库的不同版本,就成为了Go语言一个致命的缺陷。

首先我们看看Go官方对依赖管理的小历史:

  • 2012年3月 Go 1 发布,此时没有版本的概念
  • 2013年 Golang 团队在 FAQ 中提议开发者保证相同 import path 的兼容性,后来成为一纸空文
  • 2013年10月出现 Godep
  • 2014年7月出现 glide
  • 2014年有人提出 external packages 的概念,在项目的目录下增加一个 vendor 目录来存放外部的包
  • 2015年8月 Go 1.5 实验性质加入 vendor 机制
  • 2015年有人提出了采用语义化版本的草案
  • 2016年2月 Go 1.6 vendor 机制 默认开启
  • 2016年5月 Go 团队的 Peter Bourgon 建立委员会,讨论依赖管理工具,也就是后面的 dep
  • 2016年8月 Go 1.7 vendor 目录永远启用
  • 2017年1月 Go 团队发布 Dep,作为准官方试验
  • 2018年8月 Go 1.11 发布 Modules 作为官方试验
  • 2019年2月 Go 1.13 发布 Modules 将是 Go 语言默认的依赖管理工具
  • 2019年9月 Go 1.16 版本默认开启 Modules 模式

根据小历史,我们可以看出GO依赖管理分三个阶段:GOPATHGOVENDORgo module,所以Go官方为了解决这个问题,Gov1.5发布之后,首先引入了vender路径查找的解决方案。

vender路径被添加到除了GOROOTGOPATH之外的依赖目录,查找依赖包路径的的顺序如下:

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

后来Go官方继续优化依赖管理,后来在 Go1.11提出了 Modules。由于vender依赖模式比较老旧,所以我们这里主要讲的Go包的依赖管理是Modules。如果对vender模式感兴趣的兄弟可以自行谷歌查找对应的使用!推荐阅读官方文档:传送门1传送门2传送门3

柒 go module介绍

Go moduleGo1.11 版本发布的依赖管理方案,于Go1.16版本默认开启。

7.1 GO111MODULE

Go语言在 go module 的过渡阶段提供了 GO111MODULE 这个环境变量来作为是否启用 go module 功能的开关。其实在 Go1.16 之后 go module 已经默认开启,这里只是简单了解即可!

要启用go module支持首先要设置环境变量GO111MODULE,通过它可以开启或关闭模块支持,它有三个可选值:offonauto,默认值是auto

  • GO111MODULE=off:禁用模块支持,编译时会从GOPATHvendor文件夹中查找包
  • GO111MODULE=on:启用模块支持,编译时会忽略GOPATHvendor文件夹,只根据 go.mod下载依赖
  • GO111MODULE=auto:当项目在$GOPATH/src外且项目根目录有go.mod文件时,开启模块支持

设置GO111MODULE=on之后就可以使用go module了,以后就没有必要在GOPATH中创建项目了,并且还能够很好的管理项目依赖的第三方包信息。使用 go module 管理依赖后会在项目根目录下生成两个文件go.modgo.sum

7.2 GOPROXY

这个环境变量主要是用于设置 Go 模块代理,其作用是用于使 Go 在后续拉取模块版本时能够脱离传统的 VCS 方式,直接通过镜像站点来快速拉取。

Go1.13之后 GOPROXY 的默认值是:https://proxy.golang.org,direct,由于某些原因国内无法正常访问该地址,所以我们通常需要配置一个可访问的地址。目前社区使用比较多的有两个https://goproxy.cnhttps://goproxy.io,当然如果有私有的GOPROXY地址那么就直接使用。设置GOPAROXY的命令如下:

go env -w GOPROXY=https://goproxy.cn,direct

GOPROXY 允许设置多个代理地址,多个地址之间需使用英文逗号 , 分隔。最后的 direct 是一个特殊指示符,用于指示 Go 回源到源地址去抓取(比如 GitHub 等)。当配置有多个代理地址时,如果第一个代理地址返回 404410 错误时,Go 会自动尝试下一个代理地址,当遇见 direct 时触发回源,也就是回到源地址去抓取。

7.3 GOPRIVATE

设置了 GOPROXY 之后,Go 命令就会从配置的代理地址拉取和校验依赖包。当我们在项目中引入了非公开的包(内部git仓库或 github 私有仓库等),此时便无法正常从代理拉取到这些非公开的依赖包,这个时候就需要配置 GOPRIVATE 环境变量。GOPRIVATE用来告诉 Go 命令哪些仓库属于私有仓库,不必通过代理服务器拉取和校验。

GOPRIVATE 的值也可以设置多个,多个地址之间使用英文逗号 , 分隔。我们通常会把自己公司内部的代码仓库设置到 GOPRIVATE 中,例如:

$ go env -w GOPRIVATE="git.mycompany.com"

这样在拉取以git.mycompany.com为路径前缀的依赖包时就能正常拉取了。此外,如果公司内部自建了 GOPROXY 服务,那么我们可以通过设置 GONOPROXY=none,允许通内部代理拉取私有仓库的包。一般情况下,我们都会设置 GONOPROXY=none

7.4 go mod命令

对于 Go module 的使用,官方提供了以下命令:

命令 介绍
go mod init 初始化项目依赖,生成go.mod文件
go mod download 根据go.mod文件下载依赖(默认为$GOPATH/pkg/mod目录)
go mod tidy 比对项目文件中引入的依赖与go.mod进行比对
go mod graph 输出依赖关系图
go mod edit 编辑go.mod文件
go mod vendor 将项目的所有依赖导出至vendor目录下
go mod verify 检验一个依赖包是否被篡改过
go mod why 解释为什么需要某个依赖

捌 使用go module引入包

8.1 初始化项目

如何使用 go module 拉取和管理项目依赖呢?首先需要我们初始化项目,在本地创建一个名为demo的文件夹并切换到该目录下,然后执行下面命令初始化项目:

PS C:\Desktop\demo> go mod init demo
go: creating new go.mod: module demo

该命令会自动在项目目录下创建一个go.mod文件,其内容如下:

// 定义当前项目的导入路径
module demo
// 标识当前项目使用的 Go 版本
go 1.18

368e7d26079bd3a1450b3735631b0197.png

go.mod文件会记录项目使用的第三方依赖包信息,包括包名和版本,由于我们的demo项目目前还没有使用到第三方依赖包,所以go.mod文件暂时还没有记录任何依赖包信息,只有当前项目的一些基本信息。

接着我们在demo目录下创建一个main.go文件:

// demo/main.go
package main
import "fmt"
func main() {
    fmt.Println("demo")
}

然后我们来学习如何引入一个第三方包,我们以github.com/A7cc/GoHello(之前我用来测试用的)这个第三方包为例子实现引入包。我们需要先将依赖包下载到本地同时在go.mod中记录依赖信息,然后才能在我们的代码中引入对应的包。

8.2 下载第三方依赖包

下载依赖有两种方法:

  • 第一种方法是在项目目录下使用终端执行go get命令手动下载依赖的包:
    PS C:\Desktop\demo> go get -u github.com/A7cc/GoHello
    go: added github.com/A7cc/GoHello v0.1.0
    // 上面这种方法会下载最新发布的版本,如果我们需要指定版本,只需要在url的最后拼接@指定版本号即可
    PS C:\Desktop\demo> go get -u github.com/A7cc/GoHello@v0.1.0
    
    可以看到go.mod的内容添加了一行:require github.com/A7cc/GoHello v0.1.0 // indirect
    70a6198609884120ce6296baf5724e67.png
    行尾的indirect表示该依赖包为间接依赖,说明在当前程序中的所有 import 语句中没有发现引入这个包。

同时还会生成go.sum文件:
5fcc6000cf4daaef8d1b6618fdfeea20.png

如果依赖包没有发布任何版本则会拉取最新的提交,最终go.mod中的依赖信息会变成类似下面这种由默认v0.0.0的版本号和最新一次commit的时间和hash组成的版本格式:

// 这里是我举例的demo,由于已经有发布版本了所以不会出现下面这种情况
require github.com/A7cc/GoHello v0.0.0-20220820074646-45b0acd320e

如果想指定下载某个commit对应的代码,可以直接指定commit hash,不过没有必要写出完整的commit hash,一般前7位即可。例如:

// 我们通过commit下载v0.1.0版本的
PS C:\Desktop\demo> go get github.com/A7cc/GoHello@e876c7c
go: added github.com/A7cc/GoHello v0.1.0

0656afb7dfaf3d238cd861cd263583ee.png

另外在执行go get命令还有其他方法:

运行go get -u将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
运行go get -u=patch将会升级到最新的修订版本
运行go get package@version将会升级到指定的版本号version

  • 第二种方式是我们直接编辑go.mod文件,将依赖包和版本信息写入该文件。我们修改demo/go.mod文件内容如下:
    module demo
    go 1.18
    require github.com/A7cc/GoHello latest
    // latest表示对外发布的最新版本
    // 当然了如果想下载知道版本,直接将latest改为对应的版本即可
    
    然后在项目目录下使用终端执行go mod download下载依赖包:
    PS C:\Desktop\demo> go mod download
    
    如果不输出其它提示信息就说明依赖已经下载成功,此时go.mod文件已经更新到对应版本!

这种方法同样支持指定想要下载的commit进行下载,例如直接在go.mod文件中按如下方式指定commit hash,这里只写出来了commit hash的前7位,然后在项目目录下使用终端执行go mod download即可:

module demo

go 1.18

require github.com/A7cc/GoHello e876c7c

8.3 第三方依赖包的使用

下载好依赖包后,我们引入该包,使用里面的函数方法并编译运行:

// demo/main.go
package main
import (
    "fmt"
    "github.com/A7cc/GoHello"
)
func main() {
    GoHello.PrintHello()
    fmt.Println("demo")
}
// Hello World!这是Go的测试库!
// demo

Go语言支持在一个项目下定义多个包,比如我们在demo项目内部创建一个新的包——test,此时新的项目目录结构如下(必须要有go.mod文件去确定test包的位置):

demo
├ go.mod
├ go.sum
├ main.go
└ test
  └ test.go

其中demo/test/test.go文件内容如下:

// 注意这里的包是test包不是main,因为它不是完整的程序
// 这里的包名、文件名和文件夹尽量一致,因为在引入包时不容易混淆,当然也可以不一致,这时候引入包的名字也行,文件的名字也行,不过建议引入的是包名
package test

import "fmt"

func Run() {
    fmt.Println("正在运动!")
}

此时想要在当前项目目录下的其他包或者main.go中调用这个Run函数需要在main.go中引入该包,然后调用Run方法:

// demo/main.go
package main

import (
    "demo/test" // 导入当前项目下的包,这种方式引入当前文件夹的包需要在终端`go mod init 当前文件夹的名字`,即当前文件夹创建这个包
    "fmt"

    "github.com/A7cc/GoHello" // 导入github上第三方包
)

func main() {
    // 调用第三方的包
    GoHello.PrintHello()
    fmt.Println("demo")
    // 调用本地的包
    test.Run()
}

从上面的示例可以看出,项目中定义的包都会以项目的导入路径为前缀,一般是在项目当前路径下索引的。如果需要索引其他位置的包,可以使用如下方法。

首先在go.mod文件中使用replace语句将依赖临时替换为本地的代码包,例如在我的电脑上有另外一个名为Desktop/demo2的项目,它位于demo项目同级目录下:

├ holiday
│├ go.mod
│├ go.sum
│├ main.go
│└ test
│  └ test.go
└ demo2
  ├ go.mod
  └ demo2.go

我们可以在demo/go.mod文件中正常引入Desktop/demo2包,这里demo2包的内容为:

// go.mod
module demo2
go 1.18
// demo2/main.go
// 注意这里的包是demo2包不是main
package demo2

import "fmt"

func Run() {
    fmt.Println("这是demo2包!")
}

然后像下面的示例那样使用replace语句将这个依赖替换为使用相对路径表示的本地包:

module demo

go 1.18

require github.com/A7cc/GoHello v0.1.0
require Desktop/demo2 v0.0.0

replace Desktop/demo2  => ../demo2

根据这个原理我们可以使用replace将项目依赖中的某个包,替换为其他版本的代码包或我们自己修改后的本地代码包。

PS:看上面的路径可能会有点绕,但是我们只需要记住在Golang中,没有相对路径这一说,它只有相对项目路径!!!

8.4 go.mod文件

go.mod文件中记录了当前项目中所有依赖包的相关信息,声明依赖的格式如下:

require module/path v1.2.3
// require:声明依赖的关键字
// module/path:依赖包的引入路径
// v1.2.3:依赖包的版本号。支持以下几种格式:
//	latest:官方发布的最新版本
//	v1.0.0:详细版本号
//	commit hash:指定某次commit hash

引入某些没有发布过tag版本标识的依赖包时,go.mod中记录的依赖版本信息就会出现类似v0.0.0-20210218074646-139b0bcd549d的格式,由版本号、commit时间和commithash值组成:
bf8177ef19e8f9e301cc4fa3783b4f39.png

8.5 go.sum文件

使用go module下载了依赖后,项目目录下还会生成一个go.sum文件,这个文件中详细记录了当前项目中引入的依赖包的信息及其hash 值。go.sum文件内容通常是下面的格式:

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

或者:

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

eb701b2252c64a94bd42a305c6603738.png
不同于其他语言提供的基于中心的包管理机制,例如 npmpypi等,Go并没有提供一个中央仓库来管理所有依赖包,而是采用分布式的方式来管理包。为了防止依赖包被非法篡改,Go module 引入了go.sum机制来对依赖包进行校验。说白了,go.sum就是为了校验go.mod里面的包是否当前使用的包。

8.6 依赖的保存位置

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

mod
├ cache
├ cloud.google.com
├ github.com
  └ A7cc
    ├ GoHello@v0.0.0-20220820074646-45b0acd320e
    ├ GoHello@v0.1.1
    └ GoHello@v0.1.0
...

如果想清除所有本地已缓存的依赖包数据,可以执行 go clean -modcache 命令

8.7 依赖的移除

我们在代码中删除依赖代码后,相关的依赖库并不会在go.mod文件中自动移除。这种情况下我们可以使用下面这个命令更新go.mod中的依赖关系:

go mod tidy

8.8 编辑go.mod文件的依赖——go mod edit

  • 格式化

因为我们可以手动修改go.mod文件,所以有些时候需要格式化该文件。Go提供了一下命令:

go mod edit -fmt
  • 添加依赖项

    go mod edit -require=golang.org/x/text
    
  • 移除依赖项

如果只是想修改go.mod文件中的内容,那么可以运行go mod edit -droprequire=package path,比如要在go.mod中移除golang.org/x/text包,可以使用如下命令:

go mod edit -droprequire=golang.org/x/text

关于go mod edit的更多用法可以通过go help mod edit查看。

玖 使用go module发布包

9.1 上传和发布包

当我们需要发布一个自己编写的代码包到公司内部或者github.com仓库上。首先我们需要在自己的github上创建一个项目,例如创建和发布一个名为GoHello的项目:
0f089842c206adc22823a8f61b356c64.png
然后再我们本地目录创建一个GoHello项目目录,在GoHello项目目录下,初始化该项目,创建go.mod文件,需要注意的是这里定义项目的引入路径为项目的URL

go mod init github.com/A7cc/GoHello

ee8942c1bc67fcd0f8ea270c4bb6d1b3.png

接着在该项目下创建GoHello.go文件,内容为:

// GoHello.go
package GoHello

import "fmt"

func PrintHello() {
    fmt.Println("Hello World!这是Go的测试库!")
}

然后将该项目的代码 push 到自己的仓库的远端分支,这样就对外发布了一个Go包:

git init	\\ 在目录中创建新的 Git 仓库
git add.	\\ 将项目添加到仓库
git commit -m "第一次提交"	\\ 把项目提交到本地的 Git 仓库,-m 后的内容是对本次提交内容的注释

接着使用SSH加密传输将本地仓库与Github仓库建立链接,首先创建SSH KEY,在前面的终端运行:

ssh-keygen -t rsa -C "youremail@example.com"

19124fe07bfad5f1060a3ca77a40f1d9.png
在对应的目录下的.ssh目录里可以看到id_rsaid_rsa.pub这两个文件:
cfe03a0fb1b84e9190287d26c7923c66.png
登录Github找到右上角的图标,打开点进里面的Settings,再选中里面的SSH and GPG KEYS,点击右上角的New SSH keyTitle里面随便填,再把刚才id_rsa.pub里面的内容复制到Title下面的Key内容框里面,最后点击Add SSH key,这样就完成了SSH Key的加密:
31811f2669bd88c3ffe4220ce8eb054b.png

// 将本地仓库与Github仓库进行关联
git remote add origin https://github.com/A7cc/GoHello.git
// 把本地库的所有内容推送到远程仓库
git push -u origin master

这时我们就可以使用go get -u github.com/A7cc/GoHello下载并使用这个包!一个设计完善的包应该包含开源许可证及说明文档等内容,并且我们还应该维护并适时发布适当的版本,github 上发布版本号使用git tag为代码包打上标签:

// -a是标签名字
// -m是对本次提交内容的注释
git tag -a v0.1.0 -m "发布版本v0.1.0"
// 将本地的 v0.1.0 分支推送到 origin 主机的 v0.1.0 分支
git push origin v0.1.0

经过上面的操作我们就发布了一个版本号为v0.1.0的版本。在Go modules中建议使用语义化版本控制,其建议的版本号格式如下:
1b3a953e881d2508f5efabaee0735162.png

  • 主版本号:发布了不兼容的版本迭代时递增
  • 次版本号:发布了功能性更新时递增
  • 修订号:发布了bug修复类更新时递增

9.2 发布新的主版本包

假设我们现在的GoHello包需要提交交互的输入,需要传参:

package GoHello
import "fmt"
func PrintHello(name string) {
    fmt.Println("Hello World!这是Go的测试库!")
    fmt.Println("你好!用户:", name)
}

可以看到我们的修改后的项目与之前的版本并不兼容或者说改动的比较巨大影响之前版本的使用,那么我们就需要递增添加一个v2版本,做法是修改当前包的引入路径:

module github.com/A7cc/GoHello/v2
go 1.18

修改后提交代码到github.com上:

git add .
git commit -m "添加了交互"
git tag -a v2.0.0 -m "release version v2.0.0"
git push origin v2.0.0

这样在不影响使用旧版本的用户的前提下,我们新的版本也发布出去了。想要使用v2版本的代码包的用户只需按修改后的引入路径下载即可:

go get github.com/q1mi/hello/v2@v2.0.0

在代码中的实现只需要注意后面追加版本v2即可:

// demo/main.go
package main

import (
    "Desktop/demo2"
    "demo/test" // 导入当前项目下的包
    "fmt"

    "github.com/A7cc/GoHello/v2" // 引入v2版本
)

func main() {
    GoHello.PrintHello("A7cc")
    fmt.Println("demo")
    test.Run()
    demo2.Run()
}

9.3 废弃已发布版本包

如果某个发布的版本不再想让用户使用时,我们可以使用retract声明该版本是废弃版本,例如我们在GoHello/go.mod文件中按如下方式声明即可对外废弃v0.1.1版本:

module github.com/a7cc/GoHello
go 1.18
retract v0.1.1

用户使用go get下载v0.1.1版本时就会收到提示,催促其升级到其他版本。

avatar
小C&天天

修学储能 先博后渊


今日诗句