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()
相关推荐
用户1474853079748 小时前
CodeX使用Skill生成游戏美术和音乐资源,一分钟入门
后端
Melody1238 小时前
用 abort 中断 AI 流式请求,我之前做错了
后端
onething3659 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 5 —— SSE 流式输出 + 打字机效果
人工智能·后端·全栈
一个做软件开发的牛马9 小时前
MyBatis-Plus 从零实战:完整搭建可运行 Demo,BaseMapper 零 SQL、Wrapper 条件构造、分页插件与代码生成器详解
java·后端
码事漫谈9 小时前
AI 编程的「三体」架构:OpenSpec + Superpowers + GStack 如何让一个开发者撑起整个研发团队
后端
吃饱了得干活9 小时前
深入解析 OpenFeign:从重试、拦截到负载均衡的全维度实践
后端
onething3659 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 6 —— 业务完善 + 会话消息预览
人工智能·后端·全栈
BingoGo9 小时前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack10 小时前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
IT_陈寒10 小时前
SpringBoot自动配置的坑,我爬了三天才出来
前端·人工智能·后端