Go语言是这么做程序初始化的

Go应用程序的初始化是在单一的goroutine中执行的。对于包这一级别的初始化来说,在一个包里会先进行包级别变量的初始化,再进行init函数的初始化。一个包下可以有多个init函数,每个文件也可以有多个init函数,多个init函数按照它们的文件名顺序逐个初始化。但是程序不可能把所有代码都放在一个包里,通常都是会引入很多包。如果main包引入了pkg1包,pkg1包本身又导入了包pkg2,那么应用程序的初始化会按照什么顺序来执行呢?

对于这个初始化过程我粗略的画了一个示意图,理解起来更直观些 图的上半部分表示了main包导入了pkg1包,pkg1包又导入了pkg2包,这样一个包之间的依赖关系。图的下半部分表示了,这个应用初始化工作的执行时间顺序是从被导入的最深层包开始进行初始化,层层递出,最后到main包,每个包内部的初始化程序依然是先执行包变量初始化再进行init函数的执行

下面通过示例来验证一下这个初始化顺序,在go_tour目录下有三个包package1和package2和utils

代码目录如下:

复制代码
├─package1
├─package2
└─utils

分别定义测试函数,在utils包下有文件utils.go

go 复制代码
package utils

import "fmt"

func TraceLog(t string, v int) int {
   fmt.Printf("TraceLog-----%s--------%d\n", t, v)
   return v
}

package1包下有如下程序package1.go:

go 复制代码
package package1

import (
   "fmt"
   "go_tour/package2"
   "go_tour/utils"
)

var V1 = utils.TraceLog("init package1 value1", package2.Value1+10)
var V2 = utils.TraceLog("init package1 value2", package2.Value2+10)

func init() {
   fmt.Println("init func in package1")
}

package2包下有如下程序package2.go:

go 复制代码
package package2

import (
   "fmt"
   "go_tour/utils"
)

var Value1 = utils.TraceLog("init package2 value1", 20)
var Value2 = utils.TraceLog("init package2 value2", 30)

func init() {
   fmt.Println("init func1 in package2")
}

func init() {
   fmt.Println("init func2 in package2")
}

主程序main.go:

go 复制代码
package main

import (
   "fmt"
   "go_tour/package1"
   "go_tour/utils"
)

func init() {
   fmt.Println("init func1 in main")
}

func init() {
   fmt.Println("init func2 in main")
}

var MainValue1 = utils.TraceLog("init M_v1", package1.V1+10)
var MainValue2 = utils.TraceLog("init M_v2", package1.V2+10)

func main() {
   fmt.Println("main func in main")
}

运行结果:

swift 复制代码
TraceLog-----init package2 value1--------20
TraceLog-----init package2 value2--------30
init func1 in package2
init func2 in package2                     
TraceLog-----init package1 value1--------30
TraceLog-----init package1 value2--------40
init func in package1                      
TraceLog-----init M_v1--------40           
TraceLog-----init M_v2--------50           
init func1 in main                         
init func2 in main                         
main func in main   

实验与结论相符合,按照导入包的层次,最先被依赖的包最先被初始化,且初始化的顺序是先初始化包变量,再说初始化init函数。初始化过程总结如下:

  • 包级别变量的初始化先于包内init函数的执行。
  • 一个包下可以有多个init函数,每个文件也可以有多个init函数。
  • 多个init函数按照它们的文件名顺序逐个初始化。
  • 初始化工作的顺序是,从被导入的最深层包开始进行初始化,层层递出最后到main包。
  • 不管包被导入多少次,包内的init函数只会执行一次。
  • 应用在所有初始化工作完成后才会执行main函数

学习交流

如果您觉得文章有帮助,点个关注哦。可以关注公众号:IT杨秀才 ,秀才后面会在公众号分享Go语言:基础 》进阶 》探秘 》实战 》面试的系列知识。也会持续更新更多硬核文章,一起聊聊互联网那些事儿!

-----------------------------历史好文-------------------------------

《2024年必备的Go语言学习路线(建议收藏🔥)》

相关推荐
IT_陈寒1 小时前
SpringBoot性能优化完全指南
前端·人工智能·后端
随性而行3601 小时前
企业微信 Webhook 接收不到消息?网络连通性、URL 签名验签与 API 返回错误码排查指南
java·网络·人工智能·后端·微信·企业微信
相隔一个图书馆的距离1 小时前
单测(三)Spring Boot 3.5.6 && @MockitoSpyBean / AOP / 循环依赖:静默吞错与运行期 NPE
java·spring boot·后端
凤山老林1 小时前
Spring Boot 集成 Apache CXF 发布与消费 SOAP WebService:WSDL、WS-Security
spring boot·后端·apache·webservice·soap·apache cxf
我不会起名字3221 小时前
gRPC 拦截器写错:metadata 透传与超时注入的 3 个坑
微服务·go·grpc·拦截器
软件技术NINI1 小时前
基于JavaWeb 汽车租聘
java·vue.js·spring boot·后端·python·毕业设计·毕设
yz_aiks1 小时前
RestTemplate 废弃后迁移 RestClient 实战:Spring Boot 4.2 升级指南
java·spring boot·后端·微服务·restclient
金銀銅鐵11 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
念何架构之路11 小时前
go-grpc设计思想
开发语言·后端·golang
system_error_x12 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端