1. 包 (package)
Go言语是运用包来组织源代码的,包 (package) 是多个 Go 源码的调集,是一种高档的代码复用计划
Go言语中为咱们供给了许多内置包,如 fmt、os、io 等
任何源代码文件有必要归于某个包,一起源码文件的榜首行有效代码有必要是 package packageName
句子,经过该句子声明自己地点的包
1.1 包的基本概念
Go言语的包借助了目录树的组织形式,一般包的称号便是其源文件地点目录的称号,虽然Go言语没有强制要求包名有必要和其地点的目录名同名,但还是主张包名和地点目录同名,这样结构更明晰
包能够界说在很深的目录中,包名的界说是不包括目录途径的,可是包在引证时一般运用全途径引证
包的习惯用法:
- 包名一般是小写的,运用一个简略且有意义的称号
- 包名一般要和地点的目录同名,也能够不同,包名中不能包括
-
等特殊符号 - 包名为 main 的包为应用程序的进口包,编译不包括 main 包的源码文件时不会得到可履行文件
- 一个文件夹下的一切源码文件只能归于同一个包,相同归于同一个包的源码文件不能放在多个文件夹下
1.2 包的导入
要在代码中引证其他包的内容,需求运用 import 关键字导入运用的包。详细语法如下:
import "包的途径"
留意事项:
- import 导入句子通常放在源码文件最初包声明句子的下面
- 导入的包名需求运用双引号包裹起来
包的导入有两种写法,分别是单行导入和多行导入
单行导入
import "包 1 的途径"
import "包 2 的途径"
多行导入
import (
"包 1 的途径"
"包 2 的途径"
)
1.3 包的导入途径
包的绝对途径便是 GOROOT/src/
或 GOPATH
后面包的存放途径,如下所示:
import "lab/test"
import "database/sql/driver"
import "database/sql"
上面代码的含义如下:
- test 包是自界说的包,其源码坐落
GOPATH/lab/test
目录下 - driver 包的源码坐落
GOROOT/src/database/sql/driver
目录下 - sql 包的源码坐落
GOROOT/src/database/sql
目录下
1.4 包的引证格局
包的引证有四种格局,下面以 fmt 包为例来分别演示一下这四种格局。
-
规范引证格局
import "fmt"
此刻能够用
fmt.
作为前缀来运用 fmt 包中的办法,这是常用的一种办法package main import "fmt" func main() { fmt.Println("Chris Liu") }
-
自界说别号引证格局
在导入包的时候,咱们还能够为导入的包设置别号,如下所示:
import F "fmt"
其中 F 便是 fmt 包的别号,运用时咱们能够运用
F.
来代替规范引证格局的fmt.
来作为前缀运用 fmt 包中的办法package main import F "fmt" func main() { F.Println("Chris Liu") }
-
省略引证格局
import . "fmt"
这种格局相当于把 fmt 包直接合并到当时程序中,在运用 fmt 包内的办法是能够不必加前缀
fmt.
,直接引证package main import . "fmt" func main() { //不需求加前缀 fmt. Println("Chris Liu") }
-
匿名引证格局
在引证某个包时,假如仅仅期望履行包初始化的 init 函数,而不运用包内部的数据时,能够运用匿名引证格局,如下所示:
import _ "fmt"
匿名导入的包与其他办法导入的包相同都会被编译到可履行文件中
运用规范格局引证包,可是代码中却没有运用包,编译器会报错
假如包中有 init 初始化函数,则经过
import _ "包的途径"
这种办法引证包,仅履行包的初始化函数,即使包没有 init 初始化函数,也不会引发编译器报错package main import ( _ "database/sql" "fmt" ) func main() { fmt.Println("Chris Liu") }
留意:
- 一个包能够有多个 init 函数,包加载时会履行悉数的 init 函数,但并不能保证履行次序,所以不主张在一个包中放入多个 init 函数,将需求初始化的逻辑放到一个 init 函数里边
- 包不能出现环形引证的状况,比方包 a 引证了包 b,包 b 引证了包 c,假如包 c 又引证了包 a,则编译不能经过
- 包的重复引证是答应的,比方包 a 引证了包 b 和包 c,包 b 和包 c 都引证了包 d。这种场景相当于重复引证了 d,这种状况是答应的,而且 Go 编译器保证包 d 的 init 函数只会履行一次
2. init函数
Go 言语中 init 函数用于包 (package) 的初始化,该函数是 Go 言语的一个重要特性
2.1 init函数的特性
- init 函数先于 main 函数主动履行
- 每个包中能够有多个 init 函数,每个包中的源文件中也能够有多个 init 函数
- init 函数没有输入参数、回来值,也未声明,所以无法引证
- 不同包的 init 函数依照包导入的依靠关系决定履行次序
- 不管包被导入多少次,init 函数只会被调用一次,也便是只履行一次
2.2 init函数的履行次序
这张图片很明晰的反应了init函数的加载次序:
- 包加载优先级排在榜首位,先层层递归进行包加载
- 每个包中加载次序为:const > var > init,首要进行初始化的是常量,然后是变量,最终才是init函数
一句话总结:
从当时包开端,假如当时包包括多个依靠包,则先初始化依靠包,层层递归初始化各个包,在每一个包中,依照源文件的字典序早年往后履行,每一个源文件中,优先初始化常量、变量,最终初始化init函数,当出现多个init函数时,则依照次序早年往后顺次履行,每一个包完结加载后,递归回来,最终再初始化当时包!
2.3 init函数的运用场景
init 函数的运用场景还是挺多的,比方进行服务注册、进行数据库或各种中间件的初始化衔接等
Go 的规范库中也有许多地方运用到了 init 函数,比方常常运用的 pprof 工具就运用到了 init 函数,在 init 函数里边进行路由注册:
//go/1.15.7/libexec/src/cmd/trace/pprof.go
func init() {
http.HandleFunc("/io", serveSVGProfile(pprofByGoroutine(computePprofIO)))
http.HandleFunc("/block", serveSVGProfile(pprofByGoroutine(computePprofBlock)))
http.HandleFunc("/syscall", serveSVGProfile(pprofByGoroutine(computePprofSyscall)))
http.HandleFunc("/sched", serveSVGProfile(pprofByGoroutine(computePprofSched)))
http.HandleFunc("/regionio", serveSVGProfile(pprofByRegion(computePprofIO)))
http.HandleFunc("/regionblock", serveSVGProfile(pprofByRegion(computePprofBlock)))
http.HandleFunc("/regionsyscall", serveSVGProfile(pprofByRegion(computePprofSyscall)))
http.HandleFunc("/regionsched", serveSVGProfile(pprofByRegion(computePprofSched)))
}
3. *go mod
go module 是 Go 言语从 1.11 版别之后官方推出的版别办理工具,而且从 Go 1.13 版别开端,go module 成为了 Go 言语默许的依靠办理工具
Modules 官方界说为:
Modules 是相关 Go 包的调集,是源代码交换和版别操控的单元 Go言语指令直接支持运用 Modules,包括记录和解析对其他模块的依靠性Modules 替换旧的基于 GOPATH 的办法,来指定运用哪些源文件
Go 最新版别运用 go module 现已不需求设置环境变量
go mod 有以下指令:
指令 | 说明 |
---|---|
download | download modules to local cache(下载依靠包) |
edit | edit go.mod from tools or scripts (编辑go.mod) |
graph | print module requirement graph (打印模块依靠图) |
init | initialize new module in current directory (在当时目录初始化mod) |
tidy | add missing and remove unused modules(拉取缺少的模块,移除不必的模块) |
vendor | make vendored copy of dependencies(将依靠复制到vendor下) |
verify | verify dependencies have expected content (验证依靠是否正确) |
why | explain why packages or modules are needed(解释为什么需求依靠) |
- 常用的有
init tdiy edit
运用go get指令下载指定版别的依靠包:
履行go get
指令,在下载依靠包的一起还能够指定依靠包的版别
- 运转
go get -u
指令会将项目中的包升级到最新的次要版别或许修订版别 - 运转
go get -u=patch
指令会将项目中的包升级到最新的修订版别 - 运转
go get [包名]@[版别号]
指令会下载对应包的指定版别或许将对应包升级到指定的版别
提示:
go get [包名]@[版别号]
指令中版别号 能够是 x.y.z 的形式,例如 go get foo@v1.2.3 也能够是 git 上的分支或 tag,例如 go get foo@master 还能够是 git 提交时的哈希值,例如 go get foo@e3702bed2
3.1 项目中运用
-
在 GOPATH 目录下新建一个目录,并运用
go mod init
初始化生成 go.mod 文件go.mod 文件一旦创立后,它的内容将会被 go toolchain 全面掌控,go toolchain 会在各类指令履行时,比方
go get
、go build
、go mod
等修改和保护 go.mod 文件go.mod 供给了 module、require、replace 和 exclude 四个指令:
- module 句子指定包的姓名 (途径)
- require 句子指定的依靠项模块
- replace 句子能够替换依靠项模块
- exclude 句子能够忽略依靠项模块
初始化生成的 go.mod 文件如下所示:
module mypro go 1.19
-
增加依靠
新建一个 main.go 文件,写入以下代码:
package main import ( "net/http" "github.com/labstack/echo" ) func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Logger.Fatal(e.Start(":1323")) }
履行
go mod tidy
运转代码会发现 go mod 会主动查找依靠主动下载go: finding module for package github.com/labstack/echo go: found github.com/labstack/echo in github.com/labstack/echo v3.3.10+incompatible go: finding module for package github.com/stretchr/testify/assert go: finding module for package github.com/labstack/gommon/log go: finding module for package github.com/labstack/gommon/color go: finding module for package golang.org/x/crypto/acme/autocert go: found github.com/labstack/gommon/color in github.com/labstack/gommon v0.3.1 go: found github.com/labstack/gommon/log in github.com/labstack/gommon v0.3.1 go: found golang.org/x/crypto/acme/autocert in golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce go: found github.com/stretchr/testify/assert in github.com/stretchr/testify v1.7.0
go.mod中的内容:
module mypro go 1.19 require github.com/labstack/echo v3.3.10+incompatible require ( github.com/labstack/gommon v0.3.1 // indirect github.com/mattn/go-colorable v0.1.11 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect golang.org/x/text v0.3.6 // indirect )
go module 安装 package 的原则是先拉取最新的 release tag,若无 tag 则拉取最新的 commit
go 会主动生成一个 go.sum 文件来记录 dependency tree
履行脚本
go run main.go
,就能够运转项目能够运用指令
go list -m -u all
来检查能够升级的 package,运用go get -u need-upgrade-package
升级后会将新的依靠版别更新到 go.mod比方:
go get -u github.com/labstack/gommon
也能够运用
go get -u
升级一切依靠 -
一般运用包之前,是首要履行
go get
指令,先下载依靠 比方github.com/labstack/echo
运用 replace 替换无法直接获取的 package:
由于某些已知的原因,并不是一切的 package 都能成功下载,比方:golang.org 下的包
modules 能够经过在 go.mod 文件中运用 replace 指令替换成 github 上对应的库,比方:
replace (
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
)
或许
replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
go install
指令将项目打包安装为可履行文件,在安装在GOPATH的bin目录下,go install履行的项目 有必要有main办法