Go语言基于Viper封装实现

基于Viper的封装

Go语言基于Viper封装实现_第1张图片

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这样方式实现,但是最好本地也实现一个比较稳妥一点!
Go语言基于Viper封装实现_第2张图片

Go语言基于Viper封装实现_第3张图片

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文件

Go语言基于Viper封装实现_第4张图片

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,运维,java)