Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > GoLang Viper库封装

GoLang实现Viper库的封装流程详解

作者:三杯温开水

Viper是一个用于Go语言应用程序的配置管理库,它提供了一种简单而灵活的方式来处理应用程序的配置,支持多种格式的配置文件,这篇文章主要介绍了GoLang封装Viper库的流程,感兴趣的同学可以参考下文

Viper是什么

Viper是一个用于Go语言应用程序的配置管理库。它提供了一种简单而灵活的方式来处理应用程序的配置,支持多种格式的配置文件,并提供了一组API来读取和使用这些配置。

Viper支持多种配置文件格式,包括JSON、TOML、YAML和HCL等。它还支持环境变量和命令行标志等配置方式。这使得Viper非常适合需要在不同环境中运行(如开发、测试和生产环境)的应用程序,因为可以使用不同的配置文件和设置来管理应用程序的行为。 优点:使用Viper,可以轻松地将配置信息加载到应用程序中,并在需要时获取这些信息。Viper还提供了一些方便的功能,例如默认值、类型转换和键名重映射等,使得配置管理变得更加简单和灵活。

实现Viper的封装

在根目录下创建一个core文件,然后在core文件中创建多一个interal文件,这个interal文件的方法仅能让core里进行调用的,一般可以将一些仅给core内方法使用并且是需要封装的方法放置在内(调取internal包中的方法不需要添加core包名 )。如图大概就是这样的,然后就是定义一个viper的interface,实现内部的GetFileGetFiles两个方法,前者是配置信息,后者是文件夹路径。当然我们也可以使用embed这样方式实现,但是最好本地也实现一个比较稳妥一点!

interal文件夹代码实现

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 (实现本地embed标记,当然推荐使用下面本地实现的方法)

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 .
// 优先级: 命令行 > 环境变量 > 默认值
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 常量存储的环境变量是否为空
				configs = internal.ConfigsPath
				fmt.Printf("您正在使用配置默认文件夹:%s,configs的文件夹路径为%s\n", internal.ConfigsPath, configs)
			} else {
				configs = env
				fmt.Printf("您正在使用%s环境变量,configs的文件夹路径为%s\n", internal.ConfigsEnv, configs)
			}
		} else { // 命令行参数不为空 将值赋值于configs
			fmt.Printf("您正在使用命令行的-c参数传递的值,configs的文件夹路径为%s\n", configs)
		}
	} else { // path 这个切片大于0,取第一个值赋值到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()
		}
	}
	// 注册到全局
	global.Viper = v
}

根目录下创建embed.go

如果在自己练习的时候已在编辑器中配置了这个embed标记可以忽略上述本地的viper_embed.go文件(如下图配置所示),但是建议不忽略上述的viper_embed.go文件

package main
// 这些有import导报
// import (......)
func init() {
	global.Configs = configs
}
var (
	//go:embed configs
	configs embed.FS
)

global中引入结构体

global/global.go

var (
	Viper  *viper.Viper
)

到此这篇关于GoLang实现Viper库的封装流程详解的文章就介绍到这了,更多相关GoLang Viper库封装内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文