在Golang中,init函数是一个特殊的函数,用于包的初始化工作。它不需要任何参数,也没有返回值,不能在代码中显式调用,而是由Go运行时在程序启动时自动执行。理解init函数的工作机制,对掌握Go程序的初始化流程非常重要。

init函数的基本写法
每个包都可以包含任意数量的init函数,它们通常用来设置包级别的变量、校验配置或注册驱动。下面是一个简单的示例:
package main
import "fmt"
var config string
func init() {
config = "default"
fmt.Println("init 1 executed")
}
func init() {
fmt.Println("init 2 executed, config =", config)
}
func main() {
fmt.Println("main executed")
}
上面代码中定义了两个init函数,它们会在main函数之前按照出现顺序依次执行。
同一个包内的初始化顺序
Go语言规范规定,同一个包内的初始化按以下顺序进行:
- 先按照依赖关系初始化包级变量
- 再按照源文件中init函数出现的先后顺序执行
- 如果多个文件,按编译器读取文件名的顺序执行各文件中的init
包之间的初始化流程
当main包导入其他包时,被导入包的init会先于导入者执行。整体流程如下:
| 阶段 | 说明 |
|---|---|
| 依赖包变量初始化 | 从最底层被依赖的包开始初始化包级变量 |
| 依赖包init | 执行依赖包中的init函数 |
| 当前包变量 | 回到导入者包初始化自己的变量 |
| 当前包init | 执行导入者包的init函数 |
| main函数 | 最后进入main函数 |
跨包初始化示例
假设有util包和main包,util包中注册了一个组件:
// util/util.go
package util
import "fmt"
func init() {
fmt.Println("util init")
}
func Hello() string {
return "from util"
}
main包导入util包:
package main
import (
"fmt"
"ippipp.com/util"
)
func init() {
fmt.Println("main init")
}
func main() {
fmt.Println(util.Hello())
}
运行后会先打印util init,再打印main init,最后输出from util,说明依赖包先完成初始化。
init函数的常见用途
- 加载配置文件到全局变量
- 建立数据库连接池
- 向系统注册接口实现,如数据库驱动
- 运行环境检查和参数校验
注意事项
使用init函数时应注意,不要在其中写过于复杂或可能panic的逻辑,否则会导致程序无法启动。另外,如果init中产生副作用,会让单元测试难以控制,建议将核心逻辑抽成可显式调用的函数,init只做必要的轻量注册。
init函数不能有名字以外的接收者,也不能被当作普通函数赋值给变量。
通过合理使用init函数,可以让Go项目的初始化过程更清晰,也能减少main函数中堆砌准备代码的情况。