英文:
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:
main
importsalpha
andbravo
, andalpha
importscharlie
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")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论