Golang中常常用到import来导入包,有时或许也需要思考导入包的执行顺序
1、包导入原理
程序的初始化和执行都起始于main包。编译时会依次将main包中的其他包导入(如果有引用的话),当一个包被导入时,如果该包有其他包,那么依次导入其他包(类似递归),然后初始化这些包的包级常量和变量,执行init函数(如果有的话),依次类型。当main包的其他包都导入后, 初始化main包的包级常量和变量,执行init函数(如果有的话),最后执行main函数。
2、包导入的语法
2.1、导入标准库
import "fmt"
2.2、非Go Module模式下导入本地包
// student包package studentimport "fmt"func PrintlnStudent() {fmt.Println("this is student package")}/******************************************************/// main包package main// GOPATH下student所在路径import "github.com/cyj19/mytest/import-example/student"func main() {student.PrintlnStudent()}
2.3、Go Module模式下导入项目内的包
// student包package studentimport "fmt"func PrintlnStudent() {fmt.Println("this is a student package for modules")}/*****************************************************/package main// module名+包所在目录import "github.com/cyj19/mytest/example2/student"func main() {student.PrintlnStudent()}
2.4、Go Module模式下导入项目外的本地包
非GOPATH/myexample/teacher
// teacher 必须是模块modulepackage teacherimport "fmt"func PrintlnTeacher() {fmt.Println("teacher")}// mod文件module teachergo 1.17
非GOPATH/myexample/example2
// 所在项目的mod文件module example2go 1.17// 使用require指令声明teacher模块的版本require (teacher v0.0.0)// 使用replace指令声明teacher模块在本地的路径replace (teacher => ../teacher)
package mainimport "teacher"func main() {teacher.PrintlnTeacher()}
3、问题
问题描述:main包(init函数执行global中的函数)导入global包(无init函数)和server包,server包中导入了logic包,其中global包初始化读取配置文件,而logic包的包级变量的赋值使用到了配置文件中的某个字段。运行后发现logic中获取的配置文件的某个字段失败
main.go
package mainimport ("fmt""github.com/cyj19/example/global""github.com/cyj19/example/server""log""net/http")func init() {global.Init()}func main() {fmt.Printf(banner+"\n", global.Addr)server.RegisterHandle()log.Fatal(http.ListenAndServe(global.Addr, nil))}
config.go
package globalimport ("flag""fmt""github.com/fsnotify/fsnotify""github.com/spf13/viper")var Addr stringfunc Init() {initConfig()}func initConfig() {viper.SetConfigName("chatroom")viper.SetConfigType("yaml")viper.AddConfigPath(RootDir + "/config")if err := viper.ReadInConfig(); err != nil {panic(fmt.Errorf("Fatal error config file: %w \n", err))}Addr = viper.GetString("addr")}
logic.go
package logicimport ("container/ring""github.com/spf13/viper")type offlineProcessor struct {n int // 消息数量recentRing *ring.Ring // 保存所有用户最近的n条消息userRing map[string]*ring.Ring // 保存某用户离线消息}var OfflineProcessor = newOfflineProcessor()func newOfflineProcessor() *offlineProcessor {n := viper.GetInt("offline-num")return &offlineProcessor{n: n,recentRing: ring.New(n),userRing: make(map[string]*ring.Ring),}}
原因:虽然是先导入了global包,但是global包没有init函数所以只初始化了包级的常量和变量并没有执行initConfig();logic包初始化OfflineProcessor变量时执行了newOfflineProcessor函数用到viper.GetInt(“offline-num”) 但此时viper还没有读取配置文件所以获取的值为0
解决方法:global包增加init函数,执行initConfig
package globalimport ("flag""fmt""github.com/fsnotify/fsnotify""github.com/spf13/viper")var Addr stringfunc init() {Init()}func Init() {initConfig()}func initConfig() {viper.SetConfigName("chatroom")viper.SetConfigType("yaml")viper.AddConfigPath(RootDir + "/config")if err := viper.ReadInConfig(); err != nil {panic(fmt.Errorf("Fatal error config file: %w \n", err))}Addr = viper.GetString("addr")}
