go 语言中代码执行流程

臭大佬 2020-11-23 23:49:33 321
Go 
简介 go 语言中代码执行流程

说明

Go程序是通过package来组织的,每个可独立运行的程序都需要包含一个main包,它在编译之后会生成可执行文件;在这个main包中必定包含一个入口函数main,这个函数既没有参数,也没有返回值。 Go 程序中通常包含:包、常量、变量、init()、main()等元素,如果同时存在多个包,包之间存在依赖关系,每个包中存在多个 init 函数,每个文件中存在一个 init 函数,那么问题来了,他们之间的执行顺序是什么样的?通过本文我们来对它们之间的执行顺序做尽可能详尽的说明。

Go 的执行过程是如下图所示的方式:

go执行流程

下面我们在beego框架下,利用bee的输出测试一下这个流程。

文件目录结构如下:

在测试过程中,我们只关心以上圈出四个文件,最外层是 mian.go,然后同级有modelrouters两个目录,model目录目录下有init.goarticle.go两个文件里面都有 init函数,routersrouter.goinit函数。

下面是代码

main.go

/*
 * @Description:
 * @User: Vijay <1937832819@qq.com>
 * @Date: 2020-10-20 22:34:11
 */
package main

import (
    "blogApi/middlewares"
    _ "blogApi/models"
    _ "blogApi/routers"
    "fmt"

    "github.com/astaxie/beego"
)

var _ int = returnNum()

func init() {
    fmt.Println("main的init")
    // 跨域
    middlewares.CorsDomain()
    // 登录验证
    middlewares.Init()
}

// 测试初始化变量
func returnNum() int {
    fmt.Println("初始化变量")
    return 1
}

func main() {
    beego.Run()
}

blogApi\models\article.go

package models
// ...
func init() {
    fmt.Println("article.go的init")
}

blogApi\models\init.go

package models
// ...

var db interface{} = setDb()

func setDb() interface{} {
    fmt.Println("model的init.go的db初始化")
    return 111
}
func init() {
    fmt.Println("model的inti.go的init")
}

blogApi\routers\router.go

package routers
// ...
func init() {
    fmt.Println("router的init")
}

执行

执行得到如下结果:

总结

Go要求非常严格,不允许引用不使用的包。但是有时你引用包只是为了调用init函数去做一些初始化工作。此时空标识符(也就是下划线)的作用就是为了解决这个问题。

在打印中可以看出执行流程如下,引入包->初始化包变量—>执行包里面的init()->mian.go中初始化变量->init()->mian();

在mian.go 中我们引入了_ "blogApi/models"_ "blogApi/routers"初始化,blogApi/models下面又有article.goinit.goinit方法,init.go里面我们还初始化了一个db变量。可以发现:
同一个package下,变量是自上而下去初始化的,等package包里面的变量全部初始化完后,再自上而下执行init(),

根据上面特性,如果我们需要在model下初始化db,应该在最前面的文件进行初始化,然后下面的文件都可以调用,出个馊主意,比如定义一个文件名为0init.go的文件,里面写db变量,试一下效果:

blogApi\models\0init.go

package models

import "fmt"

var db interface{} = 111

func init() {
    fmt.Println("model下0init.go初始化db")
}

修改 blogApi\models\article.go

package models
func init() {
    fmt.Printf("model的article.go中能获取到db:%v\n", db)
    fmt.Println("article.go的init")
}

执行结果:

可以看出,0init.go 定义的变量,在article,go中可以获取到,这样的话,我们就可以在0init.go初始化数据库链接了,这样在整个model下都能拿到数据库句柄了。

包里面的代码初始化完后,再进行本地mian.go文件变量初始化,然后执行init,最后才去执行mian方法

init特性及作用

init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。

init 函数的作用:

  • 对变量进行初始化
  • 检查/修复程序的状态
  • 注册
  • 运行一次计算

init 函数的特性:

  • init函数不需要传入参数,也不会返回任何值。与main相比而言,init没有被声明,因此也不能被引用。

  • 每个源文件只能包含一个init函数。