GoLang實(shí)現(xiàn)Viper庫(kù)的封裝流程詳解
Viper是什么
Viper是一個(gè)用于Go語(yǔ)言應(yīng)用程序的配置管理庫(kù)。它提供了一種簡(jiǎn)單而靈活的方式來(lái)處理應(yīng)用程序的配置,支持多種格式的配置文件,并提供了一組API來(lái)讀取和使用這些配置。
Viper支持多種配置文件格式,包括JSON、TOML、YAML和HCL等。它還支持環(huán)境變量和命令行標(biāo)志等配置方式。這使得Viper非常適合需要在不同環(huán)境中運(yùn)行(如開(kāi)發(fā)、測(cè)試和生產(chǎn)環(huán)境)的應(yīng)用程序,因?yàn)榭梢允褂貌煌呐渲梦募驮O(shè)置來(lái)管理應(yīng)用程序的行為。 優(yōu)點(diǎn):使用Viper,可以輕松地將配置信息加載到應(yīng)用程序中,并在需要時(shí)獲取這些信息。Viper還提供了一些方便的功能,例如默認(rèn)值、類(lèi)型轉(zhuǎn)換和鍵名重映射等,使得配置管理變得更加簡(jiǎn)單和靈活。
實(shí)現(xiàn)Viper的封裝
在根目錄下創(chuàng)建一個(gè)core文件,然后在core文件中創(chuàng)建多一個(gè)interal文件,這個(gè)interal文件的方法僅能讓core里進(jìn)行調(diào)用的,一般可以將一些僅給core內(nèi)方法使用并且是需要封裝的方法放置在內(nèi)(調(diào)取internal包中的方法不需要添加core包名 )。如圖大概就是這樣的,然后就是定義一個(gè)viper的interface,實(shí)現(xiàn)內(nèi)部的GetFile和GetFiles兩個(gè)方法,前者是配置信息,后者是文件夾路徑。當(dāng)然我們也可以使用embed這樣方式實(shí)現(xiàn),但是最好本地也實(shí)現(xiàn)一個(gè)比較穩(wěn)妥一點(diǎn)!


interal文件夾代碼實(shí)現(xiàn)
core/interal/viper_interface.go
type IViper interface {
// GetFile 獲取文件信息
GetFile(path, filename string) io.Reader
// GetFiles 獲取配置文件夾信息
GetFiles(dir string) ([]fs.DirEntry, error)
}core/interal/viper.go
var Viper = new(viper)
type viper struct{}
func (v *viper) GetFile(path, filename string) io.Reader {
file, err := os.Open(filepath.Join(path, filename))
if err != nil {
return nil
}
defer func() {
_ = file.Close()
}()
all, err := io.ReadAll(file)
if err != nil {
return nil
}
return bytes.NewReader(all)
}
func (v *viper) GetFiles(dir string) ([]os.DirEntry, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, errors.Wrapf(err, "[viper][path:%s]獲取配置文件夾信息失敗!", dir)
}
return entries, nil
}core/interal/vuper_embed.go (實(shí)現(xiàn)本地embed標(biāo)記,當(dāng)然推薦使用下面本地實(shí)現(xiàn)的方法)
var Viper = new(viper)
type viper struct{}
func (v *viper) GetFile(path, filename string) io.Reader {
file, err := global.Configs.Open(filepath.Join(path, filename))
if err != nil {
fmt.Printf("[viper][path:%s][filename:%s]文件不存在!\n", path, filename)
return nil
}
return file
}
func (v *viper) GetFiles(dir string) ([]fs.DirEntry, error) {
entries, err := global.Configs.ReadDir(dir)
if err != nil {
return nil, errors.Wrapf(err, "[viper][embed][dir:%s]獲取配置文件夾信息失敗!", dir)
}
return entries, nil
}core/viper.go
我這里的config文件夾中的配置yaml格式如:gorm.debug.yaml
var Viper = new(_viper)
type _viper struct{}
// Initialization .
// 優(yōu)先級(jí): 命令行 > 環(huán)境變量 > 默認(rèn)值
func (c *_viper) Initialization(path ...string) {
var configs string
if len(path) == 0 {
flag.StringVar(&configs, "c", "", "choose configs dir.")
flag.Parse()
if configs == "" {
env := os.Getenv(internal.ConfigsEnv)
if env == "" { // 判斷 internal.ConfigEnv 常量存儲(chǔ)的環(huán)境變量是否為空
configs = internal.ConfigsPath
fmt.Printf("您正在使用配置默認(rèn)文件夾:%s,configs的文件夾路徑為%s\n", internal.ConfigsPath, configs)
} else {
configs = env
fmt.Printf("您正在使用%s環(huán)境變量,configs的文件夾路徑為%s\n", internal.ConfigsEnv, configs)
}
} else { // 命令行參數(shù)不為空 將值賦值于configs
fmt.Printf("您正在使用命令行的-c參數(shù)傳遞的值,configs的文件夾路徑為%s\n", configs)
}
} else { // path 這個(gè)切片大于0,取第一個(gè)值賦值到configs
configs = path[0]
}
v := viper.New()
v.AddConfigPath(configs)
entries, err := internal.Viper.GetFiles(configs)
if err != nil {
fmt.Printf("%+v\n", err)
return
}
for i := 0; i < len(entries); i++ {
if entries[i].IsDir() { // 忽略配置文件夾里的文件夾
continue
}
filename := entries[i].Name()
// 分割文件名
names := strings.Split(filename, ".")
if len(names) == 3 {
config := names[0] // 文件名
mode := names[1] // 模式
yaml := names[2] // 文件后綴
if mode != gin.Mode() {
continue
}
// 拼接
v.SetConfigName(strings.Join([]string{config, mode}, "."))
v.SetConfigType(yaml)
reader := internal.Viper.GetFile(configs, filename)
err = v.MergeConfig(reader)
if err != nil {
fmt.Printf("[viper][filename:%s][err:%v]配置文件讀取失敗!\n", filename, err)
return
}
// 讀取配置文件
err = v.ReadInConfig()
if err != nil {
fmt.Printf("[viper][filename:%s][err:%v]配置文件讀取失敗!\n", filename, err)
continue
}
// 反序列化config
err = v.Unmarshal(&global.Config)
if err != nil {
fmt.Printf("[viper][err:%v]反序列化失敗!\n", err)
continue
}
v.OnConfigChange(func(in fsnotify.Event) {
fmt.Printf("[viper][filename:%s]配置文件更新\n", in.Name)
err = v.Unmarshal(&global.Config)
if err != nil {
fmt.Printf("[viper][err:%v]反序列化失敗!\n", err)
}
})
v.WatchConfig()
}
}
// 注冊(cè)到全局
global.Viper = v
}根目錄下創(chuàng)建embed.go
如果在自己練習(xí)的時(shí)候已在編輯器中配置了這個(gè)embed標(biāo)記可以忽略上述本地的viper_embed.go文件(如下圖配置所示),但是建議不忽略上述的viper_embed.go文件

package main
// 這些有import導(dǎo)報(bào)
// import (......)
func init() {
global.Configs = configs
}
var (
//go:embed configs
configs embed.FS
)global中引入結(jié)構(gòu)體
global/global.go
var ( Viper *viper.Viper )
到此這篇關(guān)于GoLang實(shí)現(xiàn)Viper庫(kù)的封裝流程詳解的文章就介紹到這了,更多相關(guān)GoLang Viper庫(kù)封裝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang基于JWT與Casbin身份驗(yàn)證授權(quán)實(shí)例詳解
這篇文章主要為大家介紹了Golang基于JWT與Casbin實(shí)現(xiàn)身份驗(yàn)證授權(quán)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
詳解Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理
這篇文章主要為大家詳細(xì)介紹了Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11
Go語(yǔ)言panic和recover的用法實(shí)例
panic()和recover()是Go語(yǔ)言中用于處理錯(cuò)誤的兩個(gè)重要函數(shù),本文主要介紹了Go語(yǔ)言panic和recover的用法實(shí)例,panic()用于中止程序并引發(fā)panic,而recover()用于捕獲panic并恢復(fù)程序的執(zhí)行,感興趣的可以了解一下2024-01-01
golang有用的庫(kù)及工具 之 zap.Logger包的使用指南
這篇文章主要介紹了golang有用的庫(kù)及工具 之 zap.Logger包的使用指南,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Goland使用Go Modules創(chuàng)建/管理項(xiàng)目的操作
這篇文章主要介紹了Goland使用Go Modules創(chuàng)建/管理項(xiàng)目的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
通過(guò)案例簡(jiǎn)單聊聊為什么說(shuō)Go中的字符串是不能被修改的
在接觸Go這么語(yǔ)言,可能你經(jīng)常會(huì)聽(tīng)到這樣一句話(huà),對(duì)于字符串不能修改,可能你很納悶,日常開(kāi)發(fā)中我們對(duì)字符串進(jìn)行修改也是很正常的,為什么又說(shuō)Go中的字符串不能進(jìn)行修改呢,本文就來(lái)通過(guò)實(shí)際案例給大家演示,為什么Go中的字符串不能進(jìn)行修改2023-07-07
Go語(yǔ)言題解LeetCode705設(shè)計(jì)哈希集合
這篇文章主要為大家介紹了Go語(yǔ)言題解LeetCode705設(shè)計(jì)哈希集合,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12

