前言
go 语言中有一个非常神奇的函数 init ,它可以在所有程序执行开始前被执行,并且每个 package 下面可以存在多个 init 函数,我们一起来看看这个奇怪的 init 函数。
init 特性
init函数在main函数之前执行,并且是自动执行;- 每个
package中可以存在多个init函数; - 每个
package中的源文件也可以存在多个init函数; init函数没有输入参数,返回值,也没有声明,无法引用;- 不同的
package中的init函数按照包导入的依赖关系决定执行顺序; - 无论包被导入多少次,
init函数只会执行一次。
init 的执行顺序

这张图清晰反应了 init 函数的加载顺序:
- 优先级最高的是
package加载,先层层递归进行包加载 - 每个包中的加载顺序是:
const->var->init
变量的初始化顺序
针对变量的初始化顺序,GO 官方文档有一个例子

- 这个例子的初始化顺序:
d->b->c->a - 变量的初始化顺序是按照出现的顺序进行先后加载的
- 如果某个变量需要依赖其他变量,则被依赖的变量先初始化
package 中多个 init 的执行顺序
GO 官方文档对这个有专门的说明
- 如果当前包下有多个
init函数,首先按照源文件名的字典序从前往后执行 - 若一个文件中出现多个
init函数,则按照出现顺序从前往后进行执行
加载顺序总结
- 从当前包开始,如果当前包
import了多个依赖包, - 先加载依赖包,层层递归初始化各个包,
- 在每一个包中,按照源文件的字典序从前往后执行,
- 每一个源文件中, 优先初始化常量,变量,最后是
init函数, - 当出现多个
init函数时,则按照出现的顺序从前往后一次执行,
- 每一个源文件中, 优先初始化常量,变量,最后是
- 每一个包都初始化完成后,递归返回
- 在每一个包中,按照源文件的字典序从前往后执行,
- 初始化当前包。
init 的使用场景
- 服务注册
- 数据库,缓存等中间件的初始化连接
init 注意事项
- 开发时尽量不要依赖
init的顺序, - 复杂的逻辑不要使用
init函数, init函数不能在代码中被显式调用,不能被引用,- 导入包不要出现循环依赖,
- 导入包仅仅想使用这个包的
init,不使用其他方法,可以加上下划线_, - 例如:
import _ "cumsuter_package", init不应依赖 main函数里面创建的变量,因为init先于main执行。



















