1. init函数的作用
包初始化:
init
函数用于在包被导入时执行初始化任务(如配置资源、注册组件等)。可重复定义:同一个包中可以定义多个
init
函数,它们会按顺序执行。
2. init函数的执行顺序
(1)同一个Go文件中的多个init函数
按代码中的定义顺序执行。
// a.go 文件内 func init() { fmt.Println("init 1") } func init() { fmt.Println("init 2") } // 输出:init 1 → init 2
(2)同一个包中的不同文件
按文件名排序(字典序)执行各文件中的
init
。例如:
a.go
的init
先于b.go
的init
执行。
(3)不同包的init函数
按依赖关系从最深到最浅执行:
被导入的包的
init
优先于当前包的init
。若包A导入包B,包B导入包C,则执行顺序为:C → B → A。
同一层级按
import
语句的顺序执行。
3. Go程序的完整初始化顺序
被导入的包:
递归初始化其依赖的包。
初始化包级变量和常量。
执行包的
init
函数。
当前包:
初始化包级变量和常量。
执行当前包的
init
函数。
main函数:
最后执行
main
包的main
函数。
正确顺序总结:
被导入包的变量/常量 → 被导入包的init → 当前包的变量/常量 → 当前包的init → main函数
4. 其他关键规则
(1)init函数的唯一性
即使一个包被多次导入(如多个包依赖它),其
init
函数仅执行一次(包级单例)。
(2)执行环境
所有
init
函数在同一个goroutine
中按顺序执行,不存在并发问题。
(3)避免循环导入
Go禁止循环导入(如包A导入包B,包B又导入包A),需通过设计解耦。
示例代码验证
场景:
包
deep
(依赖层次最深) → 包middle
→ 包main
。
// deep/init.go package deep func init() { fmt.Println("deep init") } // middle/init.go package middle import _ "deep" func init() { fmt.Println("middle init") } // main.go package main import _ "middle" func init() { fmt.Println("main init") } func main() { fmt.Println("main") }
输出:
deep init → middle init → main init → main
常见误区纠正
错误理解:当前包的
init
先于其变量/常量初始化。正确顺序:当前包的变量/常量初始化 → 当前包的
init
执行。
错误理解:不同包的
init
按import
顺序直接执行。正确逻辑:依赖的最深包最先初始化(递归处理)。
通过理解init
的执行顺序和规则,可以避免初始化时的依赖问题,确保资源按预期正确初始化。