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
  • 这里不再举例
相关推荐
moxiaoran57535 小时前
Go语言结构体
开发语言·后端·golang
Tony Bai12 小时前
Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域,AI 流量激增!
开发语言·人工智能·后端·golang
小徐Chao努力17 小时前
Go语言核心知识点底层原理教程【变量、类型与常量】
开发语言·后端·golang
锥锋骚年17 小时前
go语言异常处理方案
开发语言·后端·golang
moxiaoran575319 小时前
Go语言的map
开发语言·后端·golang
小信啊啊19 小时前
Go语言数组
开发语言·后端·golang
IT艺术家-rookie19 小时前
golang-- sync.WaitGroup 和 errgroup.Group 详解
开发语言·后端·golang
树下水月19 小时前
Go语言编码规范
开发语言·后端·golang
laozhoy119 小时前
深入理解Golang中的锁机制
开发语言·后端·golang
moxiaoran57531 天前
Go语言的范围range
golang