在运行任何初始化函数之前,需要加载 .env 文件。

huangapple go评论84阅读模式
英文:

Need to load .env file before any init function is run

问题

我正在尝试在我的main方法中使用godotenv.Load来加载环境变量,但问题是我在其他包中有init函数,它们在环境变量从.env文件中加载之前就已经运行了,因此造成了麻烦。

有没有任何解决方法?

英文:

I'm trying to load the environment variable with calling godotenv.Load in my main method but the problem is that I have init functions in other packages and they run before the environment variables are loaded from .env file, therefore causing trouble.

Is there any workaround for this?

答案1

得分: 1

这是Golang包初始化的规范:https://go.dev/ref/spec#Package_initialization

简而言之,对于给定的包:

  1. 当前包导入的所有包按照源文件名称的词法顺序进行初始化,然后按照每个源文件中导入的顺序进行初始化。

  2. 当前包中的所有包级变量按照依赖顺序进行初始化。

  3. 然后按照源文件名称的词法顺序执行当前包中定义的所有init()函数,然后按照每个文件内的文档顺序执行。

  4. 一旦所有包都被初始化,就会调用main()函数,开始执行程序。

根据规范,
> 包初始化——变量初始化和init函数的调用——在单个goroutine中按顺序、一个包一个包地进行。

你只需要确保你加载/设置环境变量的代码位于最先初始化的包中。但要注意go fmt会按字母顺序对imports进行排序。

因此,给定一个包含4个包的程序,mainalphabravocharlie

它们之间的依赖关系如下:

  • main导入alphabravo
  • alpha导入charlie

每个包都有多个init()函数(下面是源代码),
执行go run main.go会将以下内容写入控制台:

charlie/main.go: init: 1
charlie/main.go: init: 2
charlie/main.go: init: 3
alpha/main.go: init: 1
alpha/main.go: init: 2
alpha/main.go: init: 3
bravo/main.go: init: 1
bravo/main.go: init: 2
bravo/main-.go: init: 3
main.go: init: 1
main.go: init: 2
main.go: init: 3
main(): executing

./main.go

package main

import (
	"fmt"

	"some-host/init-example/alpha"
	"some-host/init-example/bravo"
)

func init() {
	fmt.Println("main.go: init: 1")
}

func init() {
	fmt.Println("main.go: init: 2")
}

func main() {
	fmt.Println("main(): executing")
	alpha.DoSomething()
	bravo.DoSomething()
}

func init() {
	fmt.Println("main.go: init: 3")
}

./alpha/alpha.go

package alpha

import (
	"fmt"
	"some-host/init-example/charlie"
)

func init() {
	fmt.Println("alpha/main.go: init: 1")
}

func init() {
	fmt.Println("alpha/main.go: init: 2")
}

func DoSomething() {
	charlie.DoSomething()
}

func init() {
	fmt.Println("alpha/main.go: init: 3")
}

./bravo/bravo.go

package bravo

import "fmt"

func init() {
	fmt.Println("bravo/main.go: init: 1")
}

func init() {
	fmt.Println("bravo/main.go: init: 2")
}

func DoSomething() {
}

func init() {
	fmt.Println("bravo/main-.go: init: 3")
}

./charlie/charlie.go

package charlie

import "fmt"

func init() {
	fmt.Println("charlie/main.go: init: 1")
}

func init() {
	fmt.Println("charlie/main.go: init: 2")
}

func DoSomething() {
}

func init() {
	fmt.Println("charlie/main.go: init: 3")
}
英文:

Here's the spec for Golang's package initialization: https://go.dev/ref/spec#Package_initialization

In a nutshell, for a given package,

  1. All packages imported by the current package are initialized in lexical order by source file name and then in the order in which they are imported in each source file.

  2. All package-level variables in the current package are initialized in dependency order.

  3. All init() functions defined within the current package are then executed in lexical order by source file name, and then in document order within each file.

  4. Once all packages have been initialized, the main() function is invoked, starting execution of the program.

Per the spec,
> Package initialization—variable initialization and the invocation of init functions—happens in a single goroutine, sequentially, one package at a time.

All you need to do is ensure that your loading/setting of environment variable happens in a package that gets initialized first. Note however that go fmt will alphabetize your imports.

So, given a program with 4 packages, main, alpha, bravo, and charlie,

where the dependencies are:

  • main imports alpha and bravo, and
  • alpha imports charlie

and each package has multiple init() functions (source code below),
executing go run main.go writes the following to the console:

charlie/main.go: init: 1
charlie/main.go: init: 2
charlie/main.go: init: 3
alpha/main.go: init: 1
alpha/main.go: init: 2
alpha/main.go: init: 3
bravo/main.go: init: 1
bravo/main.go: init: 2
bravo/main-.go: init: 3
main.go: init: 1
main.go: init: 2
main.go: init: 3
main(): executing

./main.go

package main

import (
	"fmt"

	"some-host/init-example/alpha"
	"some-host/init-example/bravo"
)

func init() {
	fmt.Println("main.go: init: 1")
}

func init() {
	fmt.Println("main.go: init: 2")
}

func main() {
	fmt.Println("main(): executing")
	alpha.DoSomething()
	bravo.DoSomething()
}

func init() {
	fmt.Println("main.go: init: 3")
}

./alpha/alpha.go

package alpha

import (
	"fmt"
	"some-host/init-example/charlie"
)

func init() {
	fmt.Println("alpha/main.go: init: 1")
}

func init() {
	fmt.Println("alpha/main.go: init: 2")
}

func DoSomething() {
	charlie.DoSomething()
}

func init() {
	fmt.Println("alpha/main.go: init: 3")
}

./bravo/bravo.go

package bravo

import "fmt"

func init() {
	fmt.Println("bravo/main.go: init: 1")
}

func init() {
	fmt.Println("bravo/main.go: init: 2")
}

func DoSomething() {
}

func init() {
	fmt.Println("bravo/main-.go: init: 3")
}

./charlie/charlie.go

package charlie

import "fmt"

func init() {
	fmt.Println("charlie/main.go: init: 1")
}

func init() {
	fmt.Println("charlie/main.go: init: 2")
}

func DoSomething() {
}

func init() {
	fmt.Println("charlie/main.go: init: 3")
}

huangapple
  • 本文由 发表于 2022年7月28日 19:32:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/73152187.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定