Go语言中的init函数的执行时机

init函数的执行时机

  • 这个涉及到
    • init 函数的作用和执行顺序
    • 相同个文件和不同文件中以及在不同的包中init的执行顺序
    • go文件初始化的顺序

一、init 函数的作用和执行顺序

作用

  • init 函数是用于程序执行前做包的初始化的函数,比如初始化包里面的一些变量等等
  • 通常在项目工程中,用来做 Http Server的初始化,DB的初始化, redis 初始化等等中间件的初始化

执行顺序

  • 在同一个 Go 文件可以重复定义多个 init 方法, 它的执行顺序是按照代码编写的顺序依次执行
  • 在同一个 package 里面,不同文件中的 init 方法的执行是按照文件名的顺序先后执行各个文件当中的init方法的
  • 对于不同的package
    • 如果不相互依赖的话,就会按照main包里面 import 的顺序,调用各个包中的init函数
    • 存在依赖,调用顺序就变成最后被依赖的会最先被初始化
      • 比如我们的导入顺序是在main函数中依赖a模块
      • a模块又依赖b模块,b模块又依赖c模块
      • 初始化的顺序就会先初始化c这个包再初始化b这个包再初始化a这个包
      • 最后执行main方法

测试 同一个 Go 文件可以重复定义多个 init 方法

main.go

go 复制代码
package main

import (
	. "fmt"
)

// 同一个go文件中的多个init方法,按照定义的顺序执行
func init() {
	Println(1)
}

func init() {
	Println(2)
}

func main() {}
  • 运行 $ go run main.go

  • 输出

    shell 复制代码
    1
    2
  • 可见,按照 定义顺序保持一致

二、相同个文件和不同文件中以及在不同的包中init的执行顺序

2.1 测试同一个包中,不同的文件中的init执行顺序

  • init-demo/
    • go.mod
    • main.go
    • pkg/
      • a.go
      • b.go
      • pkg.go

pkg/a.go

go 复制代码
package pkg
import . "fmt"

func init() {
	Println("pkg-a")
}

pkg/b.go

go 复制代码
package pkg
import . "fmt"

func init() {
	Println("pkg-b")
}

pkg/pkg.go

go 复制代码
package pkg
import . "fmt"

var Pkg = "pkg"

func init() {
	Println("pkg-pkg")
}

main.go

go 复制代码
package main

import (
	. "fmt"
	"init-demo/pkg"
)

// 同一个go文件中的多个init方法,按照定义的顺序执行
func init() {
	Println(1)
}

func init() {
	Println(2)
}

func main() {
	println(pkg.Pkg)
}
  • 执行 $ go run main.go

  • 输出

    shell 复制代码
    pkg-a
    pkg-b
    pkg-pkg
    1
    2
    pkg
  • main包依赖pkg包,先按照pkg中包名的 ascII 码的顺序依次执行init, 再执行 main包中的任务

  • 我们知道,先执行 pkg/a.go 中的init, 再执行 pkg/b.go中的init, 再执行 pkg/pkg.go 中的init,最终执行main方法中的任务

测试 导入不同的包有和没有依赖其他包的情况

  • init-demo/
    • go.mod
    • main.go
    • pkg1/
      • pkg1.go
    • pkg2/
      • pkg2.go

pkg1/pkg.go

go 复制代码
package pkg1
import "fmt"

var Pkg1 = "pkg1"

func init() {
	fmt.Println("pkg1-pkg1")
}

pkg2/pkg.go

go 复制代码
package pkg2
import "fmt"
import "init-demo/pkg3"

var Pkg2 = "pkg2"

func init() {
	fmt.Println("pkg2-pkg2")
}

pkg3/pkg.go

go 复制代码
package pkg3
import (
	"fmt"
)

func init() {
	fmt.Println("pkg3-pkg3")
}

main.go

go 复制代码
package main

import (
	. "fmt"
	"init-demo/pkg1"
	"init-demo/pkg2"
)

// 同一个go文件中的多个init方法,按照定义的顺序执行
func init() {
	Println(1)
}

func init() {
	Println(2)
}

func main() {
	println(pkg1.Pkg1)
	println(pkg2.Pkg2)
}
  • 执行 $ go run main.go

  • 输出

    shell 复制代码
    pkg1-pkg1
    pkg3-pkg3
    pkg2-pkg2
    1
    2
    pkg1
    pkg2
  • 先执行pkg1的init,因为在main中被最先引入,优先按照import的顺序执行,接着是pkg3的init, 接着是pkg2的init

  • 因为 pkg3被pkg2引用,这样pkg3的init优先于pkg2的init执行

  • 最后是main包中的init和main函数的方法

三、go文件初始化的顺序

  • 会先初始化引入的包
  • 再初始化当前包中的常量变量
  • 接着初始化当前包中的init函数
  • 最后执行main函数
  • 注意
    • 一个包被引用多次,这个包的init函数只会执行一次 (按照之前的规则的执行顺序下的第一次)
    • 所有的 init 函数都会在同一个 goroutine 内执行
    • 注意包的循环导入问题,会出现异常: import cycle not allowed
  • 这里不再举例
相关推荐
云和数据.ChenGuang5 小时前
Golang AI 工程师路线图:从入门到实战
开发语言·人工智能·后端·golang
报错小能手7 小时前
内存管理 垃圾回收面试题
golang
tachibana21 天前
Go 语言里的 Context 是什么?
开发语言·后端·golang·go·语言
茉莉玫瑰花茶1 天前
GO [ 数据格式 ]
服务器·网络·算法·golang
土豆.exe1 天前
chunked 编码的行尾:Go、h11 与 Tomcat 各错在哪一处
开发语言·网络安全·golang·tomcat
茉莉玫瑰花茶2 天前
GO [ 标准库 ]
开发语言·后端·golang
紫洋葱hh3 天前
Go 入门基础:类型、变量与运算符
开发语言·后端·ai·golang
codeejun3 天前
每日一Go·MySQL-7、MySQL 核心原理全景图谱
开发语言·mysql·golang
acd120093 天前
Go 里 err 被不经意覆盖的几个现场
开发语言·后端·golang
倔强的小石头_4 天前
从Go+SQLite编译到浏览器开局:landlord部署、AI对局与固定地址实战
人工智能·golang·sqlite