Golang 包的初始化过程

先上结论:

  • 导入的 package 先初始化
    • 变量初始化
    • init 方法开始运行
  • 本身 package 初始化
    • 本身变量初始化
    • 本身 init 方法开始运行

下面,通过一些例子来验证上述结论。
新建一个recipe的项目,main.go文件导入引入包menu,包menu引入包colddish

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.go
package main

import (
"fmt"
"recipe/menu"
)

func init() {
fmt.Println("package main inited.")
}

func main() {
menu.ShowMenus()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// menu.go
package menu

import (
"fmt"
"recipe/colddish"
)

func init() {
fmt.Println("package menu inited.")
}

var m = func() string {
fmt.Println("variable m initialized.")
return "menu"
}()

func ShowMenus() {
colddish.ShowColddish()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// colddish.go
package colddish

import "fmt"

func init() {
fmt.Println("package colddish inited.")
}

var c = func() string {
fmt.Println("variable c initialized.")
return "colddish"
}()

var b = func() string {
fmt.Println("variable b initialized.")
return "colddish"
}()

var a = func() string {
fmt.Println("variable a initialized.")
return "colddish"
}()

func ShowColddish() {
fmt.Println("Colddish")
}

运行一下,可以看到打印的顺序如下:

1
2
3
4
5
6
7
variable c initialized. 
variable b initialized.
variable a initialized.
package colddish inited.
variable m initialized.
package menu inited.
package main inited.

如果把colddish.go中的变量更改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var c = func() string {
fmt.Println("variable c initialized.", b)
return "c"
}()

var b = func() string {
fmt.Println("variable b initialized.", a)
return "b"
}()

var a = func() string {
fmt.Println("variable a initialized.")
return "a"
}()

此时变量之间存在着依赖,来看看对应的初始化顺序是否会有改变:

1
2
3
4
5
6
7
variable a initialized. 
variable b initialized. a
variable c initialized. b
package colddish inited.
variable m initialized.
package menu inited.
package main inited.

可以看到,此时的变量初始化顺序发生了变化,其原因是变量之间存在着依赖。

总结一下就是,在运行的过程中,编译器会按声明顺序遍历所有待初始化的变量,如果变量A不依赖未被初始化的变量,变量A则会正常初始化,反之则会跳过。重复这个过程直到所有的变量都被初始化,那么整个初始化流程就走完了。对包的初始化也是如此,按照依赖层级一层层的回溯。