go语言之包和包的管理

go语言之包和包的管理

kernelplus

已于 2022-06-11 20:40:03 修改

202
收藏 1
分类专栏: golang 文章标签: golang linux
版权

golang
专栏收录该内容
3 篇文章0 订阅
订阅专栏
go语言之包和包的管理
包的导入
导入包
包的初始化
包别名
自定义包
包的管理
基于GOPATH的包管理
导入路径
项目管理
基于GOPATH和vendor的包管理
导入路径
项目管理
基于go mod的包管理
导入路径
项目管理
包的导入
go语言的包,类似于Java中的包或C++中的头文件(仅做类比),相当于程序库。一方面避免所有的程序都写在一个文件中,另一方面可以通过代码复用降低程序开发难度。

导入包
使用包之前要先导入包,通过import关键字导入包,导入有以下几种方式:

导入单个包

import "fmt" // 导入一个包
1
导入多个包(单语句,多行)

import (
"fmt"
"time" // 导入多个包
)
1
2
3
4
导入多个包(单语句,单行)可读性不好,不推荐

import ("fmt";"time") // 分号隔开
1
导入多个包(多语句)不常用,不推荐

import "fmt"
import "time"
1
2
不能重复导入

import "fmt"
import "fmt" // 重复导入,编译不过
1
2
包的初始化
init()函数是包的初始化函数,每个包文件都可以有多个init()函数。一个包可能有多个*.go源文件,每个*.go文件可以有多个init()函数。

当一个包被import语句导入时,则会执行这个函数,执行顺序是按照包导入的顺序(有依赖包时先执行依赖包),源文件有多个init()函数时自上而下执行。

包别名
在程序中引用包时可以使用包的别名,设置别名之后原包名则不可用

设置别名格式如下:

import f "pkg/test" // 设置别名为 f
f.test() // 使用别名
1
2
.别名,此时引用包内成员时无需使用包名作为限定名,示例:

import . "pkg/test"
test() // 直接调用,无需使用包名
1
2
_别名,此时不会导入包中的成员,只会执行包中的init函数,示例:

import _ "pkg/test" // 不会导入包

1
2
自定义包
import "demo1/server"一般被理解为导入包server,实际上"demo1/server"只是一个路径,路径最后一个名称server只表示目录名,意为导入server目录下的所有*.go源文件(但不包括server的子目录下的*.go文件)。因此包名可以与目录名一致也可以不一致,如果按照go的这个语法规则,单独从语句import "demo1/server"中程序员并不能知道导入的包名。

示例:

包名与目录名一致(都为server)

package main
import "demo1/server" //导入包 server

func main(){
server.Test() // 此处的server为包名,调用包server的函数Test()
}
1
2
3
4
5
6
包名与目录名不一致(包名为srv, 目录名为server)

package main
import "demo1/server" // 导入包srv

func main(){
srv.Test() // 此处的srv为包名,调用包srv的函数Test()
}
1
2
3
4
5
6
go语言的这个特点为包的命名提供了灵活性,但是如果包名与目录名不一致,则代码库的可理解性会很差,在不知道被导入源码的情况下很难通过import语句得知包名,程序的可读性很差。

因此包的自定义最好要遵循一下原则:

一个目录下只能定义一个包(语法要求)
一个包只能定义在同一个目录下(go认为不同目录下的包不是同一个包)
包名和目录名相同(语法不强制,但强烈建议)
标准库中都遵循以上原则,自定义的库最好也要遵循这个原则,否则自定义库不容易使用。

包的管理
对于一个项目来说,go中的包无非有三种:

标准库的包,存在与$GOROOT/src
自定义库,每个项目都会有多个*.go文件组成,会形成本项目的自定义包,位于项目目录下
第三方包,一般是开源的或者公司内部的工具包,它可能会被多个项目用到
标准库的包无需管理。包的管理一般是指:如何以文件的方式组织号第三方包和项目自己的源文件,字go语言诞生经历了三种管理方式。

基于GOPATH的包管理
导入路径
go去哪里找到import关键字导入的包?

go找包的顺序:

$GOROOT/src,Linux系统中默认情况GOROOT=/usr/local/go/,标准库就位于其中。go安装时设置,一般不能改变。
$GOPATH/src,一般第三方库或自定义库位于此目录,GOPATH需要手动在操作系统中设置(GO1.8版本开始默认为/home/go,之前的版本默认为空)
相对路径,形如import ./module,一般是main文件同级目录下的包,不建议使用。
go编译器会优先查找G O R O O T / s r c / , 查 不 到 再 去 GOROOT/src/,查不到再去GOROOT/src/,查不到再去GOPATH/src/下查找。因此设置第三方库名称时不要与标准库冲突,否则找到的则是标准库。通过go get下载的第三方包也是放在$GOROOT/src/目录下的,否则go无法找到包并导入。

项目管理
GOPATH下一般有三个目录:

目录 作用
$GOPATH/bin/ go install [package] 会将可执行文件放在此处
$GOPATH/pkg/ 存放go build 生成的二进制对象(*.a文件等)
$GOPATH/src/ 存放源码。项目工程代码、第三方代码
基于此,项目工程的组织管理形式可以有多种,但必须都位于$GOPATH/src/目录下:

在$GOPATH/src/下直接放置项目工程,适用于个人的简单Demo项目


在$GOPATH/src/下通过域名组织项目,适用于开源项目。比如github上的go-cache项目,项目链接为https://github.com/patrickmn/go-cache,go get下载到本地的位置是$GOPATH/src/github.com/patrickmn/go-cache/,按照项目链接组织第三方包,方便go get自动保存包,并避免命名冲突。

3. 对于企业可以通过以下方式组织项目,企业一般在内网使用gitlab管理代码,可以按照以下方式,或者在此基础调整。

 

这种早期的基于GOPATH的项目管理方式有很多缺点:

项目必须放在$GOPATH/src/目录下,想在其他位置新建项目就得修改GOPATH变量,不方便。
如果不同的项目引用相同的包/模块,其中一个项目如果修改了这个模块,那么会影响其他项目。
不支持的模块的版本管理,如果不同的项目依赖了同一个模块的不同版本,切换项目时就要同时切换版本,如果依赖的模块很多,这事就比较麻烦了
基于GOPATH和vendor的包管理
为了解决单纯依赖GOPATH管理项目的问题,go v1.6版本正式支持了vendor管理机制,我们可以在项目工程目录下创建一个vendor目录,如下结构:

 

导入路径
有了vendor机制,包的导入路径有所变化,按照以下顺序查找:

从引用文件所在的vendor路径下面搜索
如果没有找到,那么从上层目录的vendor路径下面搜索
直到src的vendor路径下面搜索
vendor查不到再查找$GOROOT/src/
上一步找不到,再查找$GOPATH/src/
项目管理
项目的组织形式仍然同前述,只是在项目内部可以有自己的依赖包。但是仍然没有完全解决前述问题,因为如果两个项目中不同的依赖包又同时依赖了同一个第三方包(工程项目大了出现概率很大),则依然会出现前述问题。

基于go mod的包管理
1.11版本引入go mod,它可以彻底解决前述问题,启用go mod需要设置环境变量GO111MODULE,设置如下:

on 开启go mod
off 关闭go mod
auto自动,在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持
导入路径
使用go mod管理项目,首先在项目的根目录执行go mod init {projectName}projectName为项目名称(若项目为第三方工具库,则命名最好按照域名规则,如github/xxx/xxx),完成项目初始化后会自动创建一个go.mod文件,go.mod用来管理第三方包。

go.mod文件格式如下

module github/zha/test

go 1.14

require (
github.com/patrickmn/go-cache v0.0.1 // indirect
github.com/aaa/go-test2 v0.0.0-20220605072926-e977b358c1c3 // indirect
)

replace (
golang.org/x/crypto => github.com/golang/crypto latest
github.com/patrickmn/go-cache => 本地项目包的地址
)
1
2
3
4
5
6
7
8
9
10
11
12
13
解释:

第一行,项目名

第二行,go的版本号,只起到说明作用

require语句,声明项目需要的第三方依赖库,格式库名 版本号。go会自动按照库名指定的连接(如https://github.com/patrickmn/go-cache)下载源码,到$GOPATH/pkg/mod/目录下。要求按照v*.*.*的格式设置tag表示版本号,则下载时回下载指定tag的代码,如果没有设置tag则下载主分支最新的代码,go.mod中的版本号则写为格式:版本号 + 日期 + 主分支最新的 commit 哈希值前缀,如v0.0.0-20220605072926-e977b358c1c3

replace语句,声明被替换的依赖库,格式 targetPkg => replacePkg,使用repacePkg替换targetPkg。比如我们想调试模块使用本地库替换github的库,或者为了下载更快我们使用一个镜像替换原库。

一般情况下需要我们在go.mod中手动配置require语句,并指定包名和版本。在编译时go会加载指定的库,但如果本地没有所指定版本的库,则go会自动下载第三方库到$GOPATH/pkg/mod/目录下。如果不手动设置go.mod,则go会自动判断依赖的第三方库并下载最新版本或者主分支最新提交的代码。go mod管理模式下GOPATH的作用就是保存go下载的第三方包。除了第三方包以外,项目内部还有很多自定义的包,要求自定义包名必须以go.mod中的项目名作为前缀,否则go无法找到,包查找顺序:

首先,在任何文件的 import时,go会在当前目录及上级目录逐级查找go.mod文件,在包含go.mod的目录再向下逐级查找包,比较mod文件中的 module 名称是否与查找的引用包路径匹配。这种查找到的都是项目的自定义包。
go mod模式也支持vendor,上述方式找不到包时,会到当前路径下的vendor目录查找。go mod vendor命令会生成一个vendor目录,并将所有第三方依赖包拷贝到vendor中(如果之前有vendor目录要先收到删除)。
然后,在$GOPATH/pkg/mod/下查找依赖包,这种找到的都是第三方包。
在$GOROOT/src/中查找,找到的都是标准库。
项目管理
go mod 支持项目工程的目录不一定要放在$GOPATH/src/ 下,放在任何地方均可;引用的第三方包则放在$GOPATH/pkg/mod/下;自定义包放在工程目录中。
————————————————
版权声明:本文为CSDN博主「kernelplus」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41923935/article/details/125229841

posted on 2022-09-28 15:17  zxddesk  阅读(153)  评论(0编辑  收藏  举报

导航