十一、包管理
11.1、package
Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如 fmt、os、io 等。任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是package pacakgeName
语句,通过该语句声明自己所在的包。
Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰。
11.1.1、包的基本使用
导入包的语法:
import "包的路径" // 双引号
导入包路径规则:
Go 程序首先在 GOROOT/src
目录中寻找包目录,如果没有找到,则会去 GOPATH/src
目录中继续寻找。比如 fmt
包是位于 GOROOT/src
目录的 Go 语言标准库中的一部分,它将会从该目录中导入。
目录结构
db包
package db
import "fmt"
func HandleMySQL() {
fmt.Println("操作MySQL数据库")
}
package db
import "fmt"
func HandleRedis() {
fmt.Println("操作redis数据库")
}
api包
package api
import "fmt"
import "mysite/db"
func RestfulAPI() {
db.HandleMySQL()
fmt.Println("RestfulAPI:MySQL数据接口")
}
package api
import "fmt"
import "mysite/db"
func RpcAPI() {
db.HandleRedis()
fmt.Println("RpcAPI:redis数据接口")
}
main
包
package main
import "mysite/api"
func main() {
// db.HandleMySQL()
// db.HandleRedis()
api.RestfulAPI()
api.RpcAPI()
}
1、包名一般是小写的,见名知意,包名中不能包含
-
等特殊符号。2、包名规范上要和所在的目录同名,也可以不同。比如
package api
改为package newApi
,代码改动为package main import newApi "mysite/api" func main() { newApi.RestfulAPI() newApi.RpcAPI() }
3、包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件。
4、一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。
5、一个包下的不同文件不能含有同名函数。
6、如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public),在Go语言中只需要将标识符的首字母大写就可以让标识符对外可见了。
7、环境变量
GO111MODULE=off
: go env -w GO111MODULE=off8、将
mysite
剪切到src
外的任何位置,都会导包失败。
11.1.2、包的导入格式
// 一次导入多个包
import (
"fmt"
"mysite/api"
)
// 设置包的别名
import F "fmt"
// 省略引用格式
import . "mysite/api"
RestfulAPI()
// 匿名导入 :在引用某个包时,如果只是希望执行包初始化的 init 函数,而不使用包内部的数据时,可以使用匿名引用格式
import _ "包名"
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
11.1.3、包的加载顺序
init()
函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。
init 函数通常被用来:
- 对变量进行初始化
- 检查/修复程序的状态
- 注册
- 运行一次计算
注意:
1、一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。
2、包不能出现环形引用的情况,比如包 a 引用了包 b,包 b 引用了包 c,如果包 c 又引用了包 a,则编译不能通过。
3、包的重复引用是允许的,比如包 a 引用了包 b 和包 c,包 b 和包 c 都引用了包 d。这种场景相当于重复引用了 d,这种情况是允许的,并且 Go 编译器保证包 d 的 init 函数
只会执行一次
。4、
init()
函数没有参数也没有返回值。init()
函数在程序运行时自动被调用执行,不能在代码中主动调用它。
11.2、go module
module是一个相关Go包的集合,它是源代码更替和版本控制的单元。
11.2.1、Go mod命令
download download modules to local cache (下载依赖的module到本地cache))
edit edit go.mod from tools or scripts (编辑go.mod文件)
graph print module requirement graph (打印模块依赖图))
init initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor make vendored copy of dependencies (将依赖复制到vendor下)
verify verify dependencies have expected content (校验依赖)
why explain why packages or modules are needed (解释为什么需要依赖)
Mod Cache 路径默认在
$GOPATH/pkg
下面:$GOPATH/pkg/mod
11.2.2、go mod流程
同样是mysite
项目,现在从src
中剪切到其它任何位置,会编译报错。用go module来解决这个问题。
(1) 首先将你的版本更新到最新的Go版本(>=1.11)
(2)通过go命令行,进入到你当前的工程目录下,在命令行设置临时环境变量go env -w GO111MODULE=off
(3)go mod init
在当前目录下生成一个go.mod
文件,执行这条命令时,当前目录不能存在go.mod
文件(有删除)。
go mod init xxx // xxx即声明的模块名
该命令在当前目录下生成go.mod文件,文件中声明了模块名称。
module xxx
go 1.16
以后相关依赖可以声明在这里,就像python的requirements
。
此时运行程序,会将相关的依赖直接下载到
GOPATH/pkg/mod
路径下。
(4)执行go mod tidy
命令,它会添加缺失的模块以及移除不需要的模块
执行后会生成go.sum
文件(模块下载条目)。添加参数-v
,例如go mod tidy -v
可以将执行的信息,即删除和添加的包打印到命令行;
将拉取的依赖存放到GOPATH/pkg/mod
路径下。
(5)执行命令go mod verify
来检查当前模块的依赖是否全部下载下来,是否下载下来被修改过。如果所有的模块都没有被修改过,那么执行这条命令之后,会打印all modules verified
。
(6)执行命令go mod vendor
生成vendor文件夹,用来区分某些库的不同版本。该文件夹下将会放置你go.mod
文件描述的依赖包,文件夹下同时还有一个文件modules.txt
,它是你整个工程的所有模块。在执行这条命令之前,如果你工程之前有vendor目录,应该先进行删除。同理go mod vendor -v
会将添加到vendor中的模块打印出来;
标签:十一,管理,Go,init,api,go,import,mod From: https://www.cnblogs.com/xiaohaoge/p/16862705.html内部包调用从初始化模块名
go.mod
所在的目录开始查找,所以导入api
包:import "xxx/api"