Golang中常常用到import来导入包,有时或许也需要思考导入包的执行顺序
1、包导入原理
程序的初始化和执行都起始于main包。编译时会依次将main包中的其他包导入(如果有引用的话),当一个包被导入时,如果该包有其他包,那么依次导入其他包(类似递归),然后初始化这些包的包级常量和变量,执行init函数(如果有的话),依次类型。当main包的其他包都导入后, 初始化main包的包级常量和变量,执行init函数(如果有的话),最后执行main函数。
2、包导入的语法
2.1、导入标准库
import "fmt"
2.2、非Go Module模式下导入本地包
// student包
package student
import "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 student
import "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 必须是模块module
package teacher
import "fmt"
func PrintlnTeacher() {
fmt.Println("teacher")
}
// mod文件
module teacher
go 1.17
非GOPATH/myexample/example2
// 所在项目的mod文件
module example2
go 1.17
// 使用require指令声明teacher模块的版本
require (
teacher v0.0.0
)
// 使用replace指令声明teacher模块在本地的路径
replace (
teacher => ../teacher
)
package main
import "teacher"
func main() {
teacher.PrintlnTeacher()
}
3、问题
问题描述:main包(init函数执行global中的函数)导入global包(无init函数)和server包,server包中导入了logic包,其中global包初始化读取配置文件,而logic包的包级变量的赋值使用到了配置文件中的某个字段。运行后发现logic中获取的配置文件的某个字段失败
main.go
package main
import (
"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 global
import (
"flag"
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
var Addr string
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")
}
logic.go
package logic
import (
"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 global
import (
"flag"
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
var Addr string
func 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")
}