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()
相关推荐
PAK向日葵1 小时前
【C++】深入浅出,理解 C++ 奇异递归模板模式(CRTP)
c++·后端·面试
z落落1 小时前
C#ArrayList 和 List<T>核心对比和数组对比
开发语言·c#·list
Cheng小攸1 小时前
实验九:防火墙安全认证和审计实验
开发语言·安全·php
不会C语言的男孩2 小时前
C++ Primer Plus 第8章:函数探幽
开发语言·c++
lzp07912 小时前
元数据驱动开发 - 面向对象编程思想的补充(上)
spring boot·后端·ui
明月_清风10 小时前
加密解密系统完全指南:原理剖析与 Go 实践
后端
方也_arkling10 小时前
【Java-Day08】static / final / 枚举
java·开发语言
风吹夏回10 小时前
Python 全局异常处理:从“满屏 try-except”到优雅兜底
开发语言·python
Chengbei1110 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构