init函数的执行时机
- 这个涉及到
- init 函数的作用和执行顺序
- 相同个文件和不同文件中以及在不同的包中init的执行顺序
- go文件初始化的顺序
一、init 函数的作用和执行顺序
作用
- init 函数是用于程序执行前做包的初始化的函数,比如初始化包里面的一些变量等等
- 通常在项目工程中,用来做 Http Server的初始化,DB的初始化, redis 初始化等等中间件的初始化
执行顺序
- 在同一个 Go 文件可以重复定义多个 init 方法, 它的执行顺序是按照代码编写的顺序依次执行
- 在同一个 package 里面,不同文件中的 init 方法的执行是按照文件名的顺序先后执行各个文件当中的init方法的
- 对于不同的package
- 如果不相互依赖的话,就会按照main包里面 import 的顺序,调用各个包中的init函数
- 存在依赖,调用顺序就变成最后被依赖的会最先被初始化
- 比如我们的导入顺序是在main函数中依赖a模块
- a模块又依赖b模块,b模块又依赖c模块
- 初始化的顺序就会先初始化c这个包再初始化b这个包再初始化a这个包
- 最后执行main方法
测试 同一个 Go 文件可以重复定义多个 init 方法
main.go
go
package main
import (
. "fmt"
)
// 同一个go文件中的多个init方法,按照定义的顺序执行
func init() {
Println(1)
}
func init() {
Println(2)
}
func main() {}
-
运行 $
go run main.go
-
输出
shell1 2
-
可见,按照 定义顺序保持一致
二、相同个文件和不同文件中以及在不同的包中init的执行顺序
2.1 测试同一个包中,不同的文件中的init执行顺序
- init-demo/
- go.mod
- main.go
- pkg/
- a.go
- b.go
- pkg.go
pkg/a.go
go
package pkg
import . "fmt"
func init() {
Println("pkg-a")
}
pkg/b.go
go
package pkg
import . "fmt"
func init() {
Println("pkg-b")
}
pkg/pkg.go
go
package pkg
import . "fmt"
var Pkg = "pkg"
func init() {
Println("pkg-pkg")
}
main.go
go
package main
import (
. "fmt"
"init-demo/pkg"
)
// 同一个go文件中的多个init方法,按照定义的顺序执行
func init() {
Println(1)
}
func init() {
Println(2)
}
func main() {
println(pkg.Pkg)
}
-
执行 $
go run main.go
-
输出
shellpkg-a pkg-b pkg-pkg 1 2 pkg
-
main包依赖pkg包,先按照pkg中包名的 ascII 码的顺序依次执行init, 再执行 main包中的任务
-
我们知道,先执行 pkg/a.go 中的init, 再执行 pkg/b.go中的init, 再执行 pkg/pkg.go 中的init,最终执行main方法中的任务
测试 导入不同的包有和没有依赖其他包的情况
- init-demo/
- go.mod
- main.go
- pkg1/
- pkg1.go
- pkg2/
- pkg2.go
pkg1/pkg.go
go
package pkg1
import "fmt"
var Pkg1 = "pkg1"
func init() {
fmt.Println("pkg1-pkg1")
}
pkg2/pkg.go
go
package pkg2
import "fmt"
import "init-demo/pkg3"
var Pkg2 = "pkg2"
func init() {
fmt.Println("pkg2-pkg2")
}
pkg3/pkg.go
go
package pkg3
import (
"fmt"
)
func init() {
fmt.Println("pkg3-pkg3")
}
main.go
go
package main
import (
. "fmt"
"init-demo/pkg1"
"init-demo/pkg2"
)
// 同一个go文件中的多个init方法,按照定义的顺序执行
func init() {
Println(1)
}
func init() {
Println(2)
}
func main() {
println(pkg1.Pkg1)
println(pkg2.Pkg2)
}
-
执行 $
go run main.go
-
输出
shellpkg1-pkg1 pkg3-pkg3 pkg2-pkg2 1 2 pkg1 pkg2
-
先执行pkg1的init,因为在main中被最先引入,优先按照import的顺序执行,接着是pkg3的init, 接着是pkg2的init
-
因为 pkg3被pkg2引用,这样pkg3的init优先于pkg2的init执行
-
最后是main包中的init和main函数的方法
三、go文件初始化的顺序
- 会先初始化引入的包
- 再初始化当前包中的常量变量
- 接着初始化当前包中的init函数
- 最后执行main函数
- 注意
- 一个包被引用多次,这个包的init函数只会执行一次 (按照之前的规则的执行顺序下的第一次)
- 所有的 init 函数都会在同一个 goroutine 内执行
- 注意包的循环导入问题,会出现异常:
import cycle not allowed
- 这里不再举例