Go包的概念
Go 语言使用包来组织源代码。包是多个Go源代码的集合,是代码复用的一种方式。 Go语言为我们提供了很多内置的包,比如fmt、io等,任何Go源码文件都必须属于一个包,并且源代码文件中的第一行有效代码必须声明包名。
package pacakgeName
包在工程中对应目录(dictionary),目录管理者这些包,共同组成go应用。在早期的go中通过GOPATH
设置go工程目录,go项目需要在该目录下,工程的树型结构管理者go的源码。
包名为main
的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件。
一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。
通过使用import
关键字导入需要使用的其他包,import 导入语句通常放在源码文件开头包声明语句的下面;导入的包名需要使用双引号包裹起来;包名是从GOPATH/src/
后开始计算的,使用/
进行路径分隔。go的内置包直接导入无需路径。
导入包时也支持相对路径../
,./
但都基与GOPATH/src/
路径下。
总的来说,Go包的特点如下:
- go 不会自动查找当前路径下的文件,必须先在
$GOAPTH
里添加自己工程的路径; - 对于自定义包中对外提供的API,名称首字母必须大写,首字母大写控制访问权限;
- 包的编译和安装时规定,
.go
文件必须存放在一个独立的文件夹下; - 引入包通过
import
关键字,该关键字提供了若干方式引入包;
GOPATH模式下,执行go build或go run时,vendor目录、GOPATH目录、GOROOT目录下可能存在依赖库(标准库、第三方库等)。引用的依赖会按照如下目录流程依次查找:
- 在当前目录的vendor目录中查找依赖包
- 如果当前目录不存在vendor目录,则到上一级目录查找。
- 重复步骤 2,直到到达 $GOPATH/src 目录
- 如果在vendor目录下没有找到依赖包,则进入$GOROOT目录下查找依赖包。
- $GOROOT目录下没有依赖包,则进入$GOPATH目录寻找依赖包。
导入包的搜索顺序,所以自定义包需要配置完整路径,否则会找不到包。具体引入方法如下:
import 项目根路径/项目内路径/所需的包
源文件的名称不影响调用函数、方法、变量等的包名。
GOPATH
GOPATH是Go语言中使用的环境变量,使用绝对路径提供项目的工作目录。编译器在运行和构建项目时将查找此目录。如果不配置GOPATH,即使在同一目录下,代码也无法通过绝对路径相互调用。
GOPATH,是构建Go工程结构的实现,如果想要构建一个基于Go语言的项目,就需要将这个项目的目录添加到 GOPATH 中。因此go工程构建时需要添加GOPATH环境变量,多个项目之间可以使用;
分隔。
Go项目在该项目目录下构建、运行和发布。 GOPATH 不同于 Java 的 Maven、Gradle 等项目管理工具。可以一键构建项目目录,实现相关依赖管理、项目构建、打包、发布等工程需求。 GOPATH 更像是一种更加标准化的路径。只有按照其规定建立项目目录,才能实现go项目的开发。
在命令行中运行go env命令可以查看go的配置环境信息。
GOPATH是Go语言早期用来处理Go语言源码和包管理的工具。 Go编程基本都是用源码编写,以GOPATH为工作目录,并有一套完整的项目目录规则。如下,
-
在 GOPATH 指定的工作目录下,代码需要保存在
$GOPATH/src
目录下; -
$GOPATH/bin
目录用于工程经过go build
、go install
或go get
等指令后,会将产生的二进制可执行文件。 -
生成的中间缓存文件会被保存在
$GOPATH/pkg
下。
GOPATH项目管理存在以下问题
- GOPATH直接管理源代码,很难构建多个项目。
- GOPATH直接管理源码,版本迭代困难,只能将
$GOPATH/src
目录的源码一整个提交。 - 使用第三方工具包时,依赖管理很困难。
GOPATH实现多个项目只能修改GOPATH的环境变量,指向新的路径。这也是开发者容易忘记的部分。在很多IDE(集成开发工具)中,都会有系统GOPATH,和项目GOPATH的区别。
在window系统中使用GOPATH只需配置环境变量即可,如下:
在linux系统中也是如此,通过
export
命令进行export GOPATH=绝对路径
使用export指令将当前目录的值设置为环境变量GOPATH。
随着Go语言的发展,GOPATH不再是主流的构建工程的规范。官方推荐了GOMOD
工具,在go的1.11版本之后无需手动配置环境变量,使用go mod全新的项目管理方式。(将在下下节列出)
Go内置命令与工具命令
无论采用哪种方式构建项目,GO内置的编译、运行、打包等命令都不会改变。
go build
:用于编译代码,go build 有很多种编译方法,如无参数编译、文件列表编译、指定包编译等,使用这些方法都可以输出可执行文件。
go build (没有参数)在编译开始时,会搜索当前目录的 go 源码,编译源码后生成当前目录名的可执行文件并放置于当前目录下。
go build [文件列表]
go build -o [name] 指定打包名称
go clean
:命令可以移除当前源码包和关联源码包里面编译生成的文件
-i 清除关联的安装的包和可运行文件,也就是通过go install安装的文件;
-n 把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的;
-r 循环的清除在 import 中引入的包;
-x 打印出来执行的详细命令,其实就是 -n 打印的执行版本;
-cache 删除所有go build命令的缓存
-testcache 删除当前包所有的测试结果
go run
命令会编译源码,并且直接执行源码的 main()函数,不会在当前目录留下可执行文件。相当于以脚本的形式运行go文件。
gofmt
代码格式化工具。传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 .go 文件,如果不传参数,会格式化当前目录下的所有 .go 文件。
go install
和go build 命令类似,但是go install 只是将编译的中间文件放在 GOPATH 的目录下,而不是项目目录下。go install 是建立在 GOPATH 上的,无法在独立的目录里使用。
go get
命令可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。
使用 go get 命令前,需要安装与远程包匹配的代码管理工具,如 Git、SVN、HG 等,参数中需要提供一个包名。
go get命令先下载源码包,然后执行 go install。工具会自动根据不同的域名调用不同的源码工具。
参数为远程包名:go get+ 远程包
,如go get github.com/davyxu/cellnet
分别表示域名/机构&作者名/项目名
go generate
运行该命令时,它将扫描与当前包相关的源代码文件,找出所有包含//go:generate的特殊注释,提取并执行该特殊注释后面的命令。
go test
命令,会自动读取源码目录下面名为 *_test.go 的文件,生成并运行测试用的可执行文件。
Go语言拥有一套单元测试和性能测试系统,仅需要添加很少的代码就可以快速测试一段需求代码。
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。要开始一个单元测试,需要准备一个 go 源码文件,在命名文件时需要让文件必须以_test结尾。
go test命令一般不需要任何的参数,它会自动把你源码包下面所有 test 文件测试完毕,当然你也可以带上参数。
单元测试源代码文件可以由多个测试用例组成,每个测试用例函数需要以Test为前缀,func TestXXX( t *testing.T )
测试用例文件不会参与正常源码编译,不会被包含到可执行文件中。 测试用例文件使用go test指令来执行,没有也不需要 main() 作为函数入口。所有在以_test结尾的源码内以Test开头的函数会自动被执行。
go pprof
Go语言工具链中的 go pprof 可以帮助开发者快速分析及定位各种性能问题,如 CPU 消耗、内存分配及阻塞分析。
GOMOD
工程管理的发展
随着Go的发展,新的项目管理工具GOMOD已经正式上线。 GOMOD弥补了GOPATH的很多缺点,为Go开发提供了极大的便利。
使用 GOPATH 的缺点:
- 代码开发必须在GOPATH src目录下。
- 依靠人工管理
- 依赖包无版本
相对而言,GOPATH 更像是一个 go 开发规范,而不是一个项目管理工具。
后来又发展到了govendor
解决了包依赖的问题,但是依赖包全都下载到项目vendor下,每个项目都把有一份,太冗余。vendor 目录下的依赖包还是需要手动加入,也没有依赖包的版本记录,那么 vendor 下的依赖包的进行升级更新也还是有困难。
go mod 在 1.11 版本中实验性地添加了 go,并在 1.13 版本之后正式成为官方的包管理工具。
go mod 增加了 go.mod 文件、GO111MODULE 变量、go build -mod 命令和 GOPATH/pkg/mod,决定了编译过程的依赖版本、依赖包存储位置和依赖包。
环境变量GO111MODULE
使用GOMOD需要先开启功能,通过go env
可以查看相关环境配置。
GO111MODULE 有三个值:off、on 和 auto(默认值)。
- GO111MODULE=off,go命令行将不支持module功能,老版本中将继续使用通过vendor目录或者GOPATH方式查找依赖包的方法。
- GO111MODULE=on,go命令行将使用模块来管理项目依赖。依赖包存放在$GOPATH/pkg/mod目录下。多个项目可以共享缓存的模块。
- GO111MODULE=auto,默认值,go命令行会根据当前目录决定是否启用模块功能。
启用modules功能后,依赖包的存储位置更改为$GOPATH/pkg,允许同一包的多个版本共存,多个项目可以共享缓存的模块。
go的环境命令go env
提供了参数可以修改GOMOD的状态,如下:
go env -w GO111MODULE=on 或者 go env -w GO111MODULE=auto
在go项目开发时需要下载各种依赖包,由于go的服务器在国外,因此需要使用代理
GOPROXY
配置go依赖库的国内镜像下载地址,否者使用国外地址会非常卡。(GOPROXY章节叙述)
Go包管理
go中的包分为三种类型:1.系统内置包2.自定义包3.第三方包。
对于前两种go mod提供了,内置和自定义的引入比较方便,直接引入包名即可,第三方包需要下载到本地,通过go get,go download
即可。
第三方包可以在https://pkg.go.dev/上寻找,是和NPM类似的包管理仓库。
GOMOD工具命令
发展到go mod才算一个项目管理的工具,使用 go mod 的工程,需要使用go mod 命令行来构建项目,且项目最明显额特征是有一个go.mod
文件。
使用go mod包管理工具完成go mod的初始化、检查依赖文件更新、自动创建vendor目录等。
表达:go mod <command> [arguments]
// 常用命令
go mod init // 初始化 go.mod,将开启 mod 使用
go mod tidy // 添加或者删除 modules,取决于依赖的引用
go mod vendor // 复制依赖到 vendor 目录下
// 其它命令
go mod download // 下载 module 到本地
go mod edit // 编辑 go.mod
go mod graph // 打印 modules 依赖图
go mod verify // 验证依赖
go mod why // 解释依赖使用
go mod init
命令来创建一个go.mod文件来管理项目,如在项目project下初始化项目:go mod init project
一个项目中必须要有main包和main方法。
当使用 go mod 的工程放在 GOPATH/src 目录下,可以直接用 go mod init 进行初始化,将自动检测 $GOPATH/src 后的目录作为包的 module
工程在 GOPATH 之外使用 go mod,在进行 mod 初始化时,需要给当前工程指定 moudle 目录
go mod download
下载 go.mod 文件中指明的所有依赖。go download [-x] [-json] [modules]
可以指定
path@version
形式的下载包,也可以不带参数表示require
元素中所有的依赖。
go mod tidy
整理现有的依赖,使用此命令来下载指定的模块,并删除已经不用的模块。
go mod graph
查看现有的依赖结构。
go mod edit
编辑 go.mod 文件,之后通过 download下载
go mod verify
查询模块是否出错
go mod why
查看包依赖源
go mod vendor
导出项目所有的依赖到vendor目录,从mod中拷贝到项目的vendor目录。
go get
和go mod download
均可以下载依赖包,它们的主要区别式前者是go提供的下载命令,能够将依赖包,下载到指定目录,便于开发者引用;后者是go mod提供的命令,在moudles环境下下载依赖包,实现自动化管理。
另外在使用时也有区别,go get xxx@xxx是下载的方式,而go mod download下载是需要可以先引入go mod文件中直接用go download下载,也可以通过go download path@version下载。
了解go.mod
文件
go mod 使用go.mod文件管理以来的版本,go.mod文件有如下几个元素,
module xx/xx/xx/v2
go 1.16
require (
xx/xx/xx v1.3.3
xx/xx/xx v0.0.0-20200330080233-e4ea8bd1cbed
xx/xx/xx v2.2.1+incompatible
xx/xx/xx v0.3.0 // indirect
)
exclude (
xx/xx/xx v1.3.3-rc.0
)
replace xx/xx/xx => xx/xx v1.3.3
retract (
v1.0.0 // 废弃的版本,请使用v1.1.0
)
各元素的具体含义为:
module xx/xx/xx/v2是指该项目的module路径,/v2是指版本信息,可以省略。
go 1.16是指项目需要的最低go的版本
require()是项目需要的其他依赖:
-- xx/xx/xx v1.3.3指明了项目需要的依赖以及版本号。
-- xx/xx/xx v0.0.0-20200330080233-e4ea8bd1cbed是时间戳性质的版本号。
-- xx/xx/xx v0.3.0 // indirect是指依赖需要的依赖
-- xx/xx/xx v2.2.1+incompatible是指依赖的库的major版本大于引用的版本,不合规范。
exclude()指引用依赖时,跳过某些版本
replace xx/xx/xx => xx/xx v1.3.3指替换某些依赖
retract()声明废弃的版本
go会自动生成一个go.sum文件来记录依赖树。 go.mod 的一个功能就是指定特定的版本,以便项目组中的每个开发人员都可以使用相同的版本号进行开发。
GOPROXY与版本迭代
Proxy就是代理服务器的意思。国内网络有防火墙,这意味着Go语言的一些第三方包无法直接通过go get命令获取。 GOPROXY是Go语言官方提供的一种通过中间代理为用户提供包下载服务的方式。要使用GOPROXY,您只需将GOPROXY设置为代理地址即可。
国内使用go的配置代理,最常用的就是七牛维护的镜像goproxy.cn
,相关配置变量如下
go env -w GOPROXY=https://goproxy.cn,direct
默认下载地址时go官方地址
使用上述命令修改为国内镜像
Java库的版本迭代一般会将Java源代码打包成jar文件上传到本地仓库或者中央仓库。对于go来说,不需要代码仓库。 Go以源码的形式实现版本迭代,只需将源码上传到git仓库即可。
开发完一个go应用后通过git实现代码仓库管理和版本迭代。git管理代码只需要将源码上传到远程仓库即可。Go模块通过git提供的Tag
标签给模块标记版本号。
# 创建一个标签
git tag <your_tag_name>
# 创建一个带有注释的标签
git tag -a <your_tag_name> -m 'your_tag_description'
# 列出所有的标签
git tag --list
# 创建一个标签
git tag <your_tag_name>
# 删除一个标签
git tag -d <your_tag_name>
# 删除远程仓库的标签
git push --delete origin <your_tag_name>
# 推送一个标签到远程
git push origin <your_tag_name>
# 推送多个本地标签到远程
git push origin --tags
当版本号没有被标记时,它将是一个伪版本号。
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
当我们通过Git打标签时,它可能看起来像这样:
golang.org/x/lint v0.0.1
GO模块的开发者使用模块版本号的各个部分来指示版本的稳定性和向后兼容性。对于每个新版本,模块的发行版本号具体反映了自上一版本以来模块的更改性质。