英文:
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
简而言之,对于给定的包:
- 
当前包导入的所有包按照源文件名称的词法顺序进行初始化,然后按照每个源文件中导入的顺序进行初始化。 
- 
当前包中的所有包级变量按照依赖顺序进行初始化。 
- 
然后按照源文件名称的词法顺序执行当前包中定义的所有 init()函数,然后按照每个文件内的文档顺序执行。
- 
一旦所有包都被初始化,就会调用 main()函数,开始执行程序。
根据规范,
> 包初始化——变量初始化和init函数的调用——在单个goroutine中按顺序、一个包一个包地进行。
你只需要确保你加载/设置环境变量的代码位于最先初始化的包中。但要注意,go fmt会按字母顺序对imports进行排序。
因此,给定一个包含4个包的程序,main、alpha、bravo和charlie,
它们之间的依赖关系如下:
- main导入- alpha和- bravo,
- 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,
- 
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. 
- 
All package-level variables in the current package are initialized in dependency order. 
- 
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.
- 
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:
- mainimports- alphaand- bravo, and
- alphaimports- 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")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。



评论