很多人第一次看到:
go
func init()
都会懵。
因为:
它:
txt
没人调用
但:
程序却自动执行了。
甚至:
很多框架源码:
全是 init()。
今天:
我们真正理解:
txt
Go 程序启动流程
+
init() 为什么会自动执行
一、init() 是什么
一句话:
txt
Go 的自动初始化函数
特点:
txt
不需要手动调用
程序启动时自动执行
二、最简单例子
go
package main
import "fmt"
func init() {
fmt.Println("init 执行")
}
func main() {
fmt.Println("main 执行")
}
三、输出结果
txt
init 执行
main 执行
四、为什么 init() 自动执行
因为:
Go 编译器:
会在程序启动时:
自动调用它。
五、Go 程序真正启动流程(重点)
很多新人:
以为:
程序直接从:
go
main()
开始。
其实不是。
真正流程:
txt
1. 加载包
2. 初始化全局变量
3. 执行 init()
4. 执行 main()
六、完整流程图(重点)
txt
程序启动
↓
导入 package
↓
初始化全局变量
↓
执行 init()
↓
执行 main()
七、为什么需要 init()
因为:
很多东西:
需要:
txt
程序启动时自动初始化
例如:
数据库连接
Redis 连接
Gin 引擎
配置文件
注册路由
八、你项目里的 init()
例如:
go
func init() {
GE = gin.Default()
corsConfig := cors.DefaultConfig()
GE.Use(cors.New(corsConfig))
}
九、这里到底发生了什么
程序启动时:
Go:
自动执行:
go
init()
十、于是自动完成
创建 Gin 引擎
go
GE = gin.Default()
配置 CORS
go
cors.DefaultConfig()
注册中间件
go
GE.Use(...)
十一、所以 main() 里不用再写
因为:
程序启动时:
已经:
txt
自动初始化好了
十二、为什么很多框架喜欢 init()
因为:
框架:
希望:
txt
用户导入包后直接能用
十三、最经典场景:数据库初始化
例如:
go
package database
import "fmt"
var DB string
func init() {
fmt.Println("连接数据库")
DB = "mysql connection"
}
十四、main.go
go
package main
import (
"fmt"
"project/database"
)
func main() {
fmt.Println(database.DB)
}
十五、执行流程(重点)
第一步
导入:
go
import "project/database"
第二步
Go:
发现:
txt
database 包里有 init()
第三步
自动执行:
go
init()
第四步
数据库初始化完成。
第五步
最后:
才执行:
go
main()
十六、所以输出
txt
连接数据库
mysql connection
十七、真正核心理解(重点)
init():
本质:
就是:
txt
包加载时的自动初始化逻辑
十八、init() 和 main() 区别
| 函数 | 作用 |
|---|---|
| init() | 自动初始化 |
| main() | 程序正式入口 |
十九、为什么 init() 没参数没返回值
Go 规定:
go
func init()
必须:
没参数
没返回值
名字固定
二十、为什么不能手动调用
例如:
go
init()
这是:
txt
非法的
二十一、因为 init 是特殊函数
Go:
会:
txt
自动管理
不允许:
手动调用。
二十二、一个包可以有多个 init()
例如:
a.go
go
func init() {
fmt.Println("a init")
}
b.go
go
func init() {
fmt.Println("b init")
}
二十三、程序会怎样
两个:
都会执行。
二十四、执行顺序
同一个包:
txt
按文件编译顺序
执行。
但:
一般:
不要依赖这个顺序。
二十五、多个包的 init() 顺序(重点)
这是:
真正重要的地方。
二十六、执行规则
txt
先初始化被导入包
再初始化当前包
最后 main()
二十七、例子(重点)
main.go
go
import "project/db"
db 包
go
func init() {
fmt.Println("db init")
}
main 包
go
func init() {
fmt.Println("main init")
}
func main() {
fmt.Println("main")
}
二十八、输出
txt
db init
main init
main
二十九、为什么这样设计
因为:
主程序:
可能依赖:
txt
db 包初始化完成
三十、Go 的包初始化顺序(必须记住)
txt
先递归初始化依赖包
↓
再初始化当前包
↓
最后执行 main
三十一、真正开发中 init() 常见用途
1. 初始化数据库
go
gorm.Open()
2. 初始化 Redis
go
redis.NewClient()
3. 初始化 Gin
go
gin.Default()
4. 注册路由
go
router.GET(...)
5. 加载配置文件
go
ini.MapTo(...)
三十二、为什么很多人不推荐大量使用 init()
因为:
它:
txt
太隐式
三十三、什么叫隐式
你:
看 main()
可能:
根本不知道:
txt
哪里初始化了数据库
因为:
它偷偷在 init() 执行了。
三十四、所以大型项目经常改成
go
func InitDB()
func InitRedis()
func InitRouter()
然后:
main():
手动调用。
三十五、为什么这样更好
因为:
txt
逻辑清晰
你一眼:
就知道:
程序启动流程。
三十六、什么时候适合 init()
适合:
简单初始化
注册操作
配置默认值
小项目
三十七、什么时候不适合
例如:
复杂数据库逻辑
大量依赖关系
大型项目
三十八、真正核心一句话(必须记住)
init():
本质:
txt
包加载时自动执行的初始化函数
真正流程:
txt
导入包
↓
初始化全局变量
↓
执行 init()
↓
执行 main()
你的项目里:
go
GE = gin.Default()
之所以自动执行:
本质:
是:
txt
Go 在包初始化阶段
自动调用了 init()