Go 里的 init() 到底是什么(彻底理解)

很多人第一次看到:

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()
相关推荐
犀利豆1 小时前
AI in Harness(三)
人工智能·后端
多加点辣也没关系2 小时前
JavaScript|第4章:类型转换
开发语言·javascript
聪慧的水蜜桃2 小时前
【YFIOs】用C#开发硬件之设备上云
开发语言·c#
yqcoder2 小时前
httpOnly 是什么,又有什么用?
开发语言·前端·javascript
IT_陈寒2 小时前
Java的Stream.parallel()把我CPU跑爆了,这种优化要谨慎
前端·人工智能·后端
子兮曰3 小时前
Bun 重写为 Rust:11天、64个Claude、16.5万美元,一次改变行业认知的激进实验
前端·后端·bun
子兮曰3 小时前
Node.js v26.5.0 深度解读:import Text、流式 ReadableStreamTee、以及隐藏的安全加固浪潮
前端·后端·node.js
子兮曰3 小时前
TypeScript 7.0 RC 深度解读:Go 重写完成,10 倍提速,编译器的「奇点时刻」
前端·后端·typescript
kosan3 小时前
.NET/C# 实战:我如何把登录验证码从“裸奔”改到“铜墙铁壁”
后端
wuqingshun3141593 小时前
重写equals而不重写hashCode,会出什么问题?
java·开发语言