七叶笔记 » golang编程 » Golang:模块初始化

Golang:模块初始化

南京总统府

一般来说,我们通常会讲Go程序的入口函数是main,是否还有比main函数更早得到运行的函数呢?

这篇文档就会讲到关于初始化函数init,尽量言简意赅以例子的形式展开。

init函数

Finally, each source file can define its own niladic init function to set up whatever state is required. (Actually each file can have multiple init functions.) And finally means finally: init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.
Besides initializations that cannot be expressed as declarations, a common use of init functions is to verify or repair correctness of the program state before real execution begins.

每个模块都可以有一个或者多个init函数,它们的特性和作用是:

  • init函数的signature是无参数,无返回值
  • 初始化模块(文件)中全局变量声明无法初始化的复杂类型:map,slice,array等
  • 模块中仅执行一次的操作,如:数据库初始化,cache初始化,读取配置文件等
  • init执行先于main
  • 只能隐式调用,不支持显式调用
  • 每个模块(或者包)中可以有多个init函数
  • 模块中任意文件可以包含多个init函数
  • 模块内的多个init函数执行顺序Golang语言未定义
  • 不同模块间init函数按照相互依赖的顺序执行

init函数的signature是无参数,无返回值

init函数的原型是一个无参数,无返回值的函数。

初始化模块(文件)中全局变量声明无法初始化的复杂类型

不是所有的变量都可以通过变量声明进行达到我们想要的初始化目的,对于这种特殊情况下的变量初始化,我们就可以通过init函数达到目的:

 package main

import (
        "fmt"
)

var aInt int = 10
var aSliceInt [12]int

func init() {
        for i := 0; i < len(aSliceInt); i++ {
                aSliceInt[i] = i
        }
}

func main() {
        fmt.Println(aSliceInt)
}  

输出:

 ➜  init git:(master) ✗ go run complicate_type.go 
[0 1 2 3 4 5 6 7 8 9 10 11]  

模块中仅执行一次的操作

有时候我们需要在模块中进行一次底层基础设施的初始化,比如数据库,cache等,对于这种操作,我们都可以放进init函数中进行处理。

init执行先于main

init完成模块的初始化操作,故执行顺序早于入口点函数main。

 package main

import (
        "fmt"
)

var aInt int = 10
var aSliceInt [12]int

func init() {
        fmt.Println("Execute point init")
        for i := 0; i < len(aSliceInt); i++ {
                aSliceInt[i] = i
        }
}

func main() {
        fmt.Println("Execute point main")
        fmt.Println(aSliceInt)
}  

输出:

 ➜  init git:(master) ✗ go run complicate_type.go 
Execute point init
Execute point main
[0 1 2 3 4 5 6 7 8 9 10 11]  

init只能隐式调用,不支持显式调用

init函数是隐式的被Golang编译器编译执行的,显式调用它会报错:

 package main

import (
        "fmt"
)

var aInt int = 10
var aSliceInt [12]int

func init() {
        fmt.Println("Execute point init")
        for i := 0; i < len(aSliceInt); i++ {
                aSliceInt[i] = i
        }
}

func main() {
        fmt.Println("Execute point main")
        fmt.Println(aSliceInt)
        init()
}  

输出:

 ➜  init git:(master) ✗ go run complicate_type.go 
# command-line-arguments
./complicate_type.go:20:2: undefined: init  

每个模块(包)中可以有多个init函数

每个模块中可以包含多个init函数,用于完成各自的目的。

sandbox.go:

 package main

import (
        "fmt"
)

func init() {
        fmt.Println("In sandbox init")
}

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

core.go:

 package main

import (
        "fmt"
)

func init(){
        fmt.Println("In core init")
}  

输出:

 ➜  init git:(master) ✗ go run sandbox.go core.go 
In sandbox init
In core init
In main  

模块中任意文件可以包含多个init函数

每个源文件都可以包含任意多个init函数,一种解释是这种做法可以让代码更加整洁和模块化,如果初始化工作过大且放在一个函数中,那么代码可读性大大降低,另外一种替代方案是把各个独立的初始化工作封装成函数,然后在放在init函数中执行,但是这同样会带来一个执行依赖的问题。当然,这个特性只是Golang语言提供的一个可选方案,大部分情况我们只需要使用一个init即可。想了解更多,可参考:

What’s the purpose of golang allowing multiple init in one package?stackoverflow.com:

下面我们通过一小段示例代码看下这个特性:

 package main

import (
        "fmt"
)

func init() {
        fmt.Println("a init")
}

func init() {
        fmt.Println("b init")
}

func init() {
        fmt.Println("c init")
}

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

输出:

 ➜  init git:(master) ✗ go run moreInit.go 
a init
b init
c init
In main  

模块内的多个init函数执行顺序Golang语言未定义

模块内多个源文件的init函数执行顺序并未在Golang中定义,所以我们在编码时不能假设有任何依赖关系。

实际上,通过下面我们可以看出,具体的执行顺序和编译语句中源文件的指定顺序有关,但是在真实产品中,我们千万不要依赖这种顺序:

 ➜  init git:(master) ✗ go run core.go sandbox.go duck.go 
In core init
In sandbox init
In duck init
In main
➜  init git:(master) ✗ go run core.go duck.go sandbox.go     
In core init
In duck init
In sandbox init
In main  

不同模块间init函数按照相互依赖的顺序执行

不同模块间init函数的执行顺序,是按照依赖关系来确定的,被依赖的模块先执行。

main.go

 package main

import (
        "fmt"
        _ "./packa"
        _ "./packb"
        _ "./packc"
)

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

packa/pack_a.go

 package packa

import (
        "fmt"
        _ "../packb"
)

func init() {
        fmt.Println("In packa init")
}  

packb/pack_b.go

 package packb

import (
        "fmt"
        _ "../packc"
)

func init() {
        fmt.Println("In packb init")
}  

packc/pack_c.go

 package packc

import "fmt"

func init() {
        fmt.Println("In packc init")
}  

输出:

 ➜  init git:(master) ✗ go run main.go     
In packc init
In packb init
In packa init
In main  

相关文章