beego使用及源码解析二(config配置模块)

目录

一.config模块的使用

1.beego使用config解析

2.自由使用beego的config模块

二:beego使用beego的config模块源码解析

三:config模块源码解析


一.config模块的使用

1.beego使用config解析

beego使用自身的config模块,一般就是再目录结构conf下,创建一个app.conf文件,如上图所示,然后配置各个选项即可。

那么我们代码中,如何获取配置文件中对应的值呢?

通过下面的方式即可获取:

func main() {
	appName :=beego.AppConfig.String("appname")
	beego.Debug("app name is:", appName)
	beego.Run()
}

2.自由使用beego的config模块

我们也可以自由定义其他配置文件,然后使用config模块

func main() {
	mycfg, err := config.NewConfig("json", "./config.json")
	if err != nil {
		beego.Error("new config err:", err)\
		return
	}
	mytestData := mycfg.String("mytest")
	beego.Debug("my test data is:", mytestData)
}

我们可以使用很多格式的配置文件:env,xml,yaml,ini,json等。

二:beego使用beego的config模块源码解析

 

我们在使用的时候一般都是在conf下搞一个app.conf的配置文件,对beego进行配置,beego就能加载这个配置文件,最后在使用时生效。

今天就来梳理这个beego配置到生效的过程。

看到beego/config.go有初始化的配置

func init() {
	BConfig = newBConfig()
	var err error
	if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil {
		panic(err)
	}
	workPath, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	var filename = "app.conf"
	if os.Getenv("BEEGO_RUNMODE") != "" {
		filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
	}
	appConfigPath = filepath.Join(workPath, "conf", filename)
	if !utils.FileExists(appConfigPath) {
		appConfigPath = filepath.Join(AppPath, "conf", filename)
		if !utils.FileExists(appConfigPath) {
			AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()}
			return
		}
	}
	if err = parseConfig(appConfigPath); err != nil {
		panic(err)
	}
}

BConfig结构如下

type Config struct {
	AppName             string //Application name
	RunMode             string //Running Mode: dev | prod
	RouterCaseSensitive bool
	ServerName          string
	RecoverPanic        bool
	RecoverFunc         func(*context.Context)
	CopyRequestBody     bool
	EnableGzip          bool
	MaxMemory           int64
	EnableErrorsShow    bool
	EnableErrorsRender  bool
	Listen              Listen
	WebConfig           WebConfig
	Log                 LogConfig
}
type Listen struct {
	Graceful          bool // Graceful means use graceful module to start the server
	ServerTimeOut     int64
	ListenTCP4        bool
	EnableHTTP        bool
	HTTPAddr          string
	HTTPPort          int
	AutoTLS           bool
	Domains           []string
	TLSCacheDir       string
	EnableHTTPS       bool
	EnableMutualHTTPS bool
	HTTPSAddr         string
	HTTPSPort         int
	HTTPSCertFile     string
	HTTPSKeyFile      string
	TrustCaFile       string
	EnableAdmin       bool
	AdminAddr         string
	AdminPort         int
	EnableFcgi        bool
	EnableStdIo       bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O
}

// WebConfig holds web related config
type WebConfig struct {
	AutoRender             bool
	EnableDocs             bool
	FlashName              string
	FlashSeparator         string
	DirectoryIndex         bool
	StaticDir              map[string]string
	StaticExtensionsToGzip []string
	TemplateLeft           string
	TemplateRight          string
	ViewsPath              string
	EnableXSRF             bool
	XSRFKey                string
	XSRFExpire             int
	Session                SessionConfig
}

// SessionConfig holds session related config
type SessionConfig struct {
	SessionOn                    bool
	SessionProvider              string
	SessionName                  string
	SessionGCMaxLifetime         int64
	SessionProviderConfig        string
	SessionCookieLifeTime        int
	SessionAutoSetCookie         bool
	SessionDomain                string
	SessionDisableHTTPOnly       bool // used to allow for cross domain cookies/javascript cookies.
	SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
	SessionNameInHTTPHeader      string
	SessionEnableSidInURLQuery   bool // enable get the sessionId from Url Query params
}

// LogConfig holds Log related config
type LogConfig struct {
	AccessLogs       bool
	EnableStaticLogs bool   //log static files requests default: false
	AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
	FileLineNum      bool
	Outputs          map[string]string // Store Adaptor : config
}

BConfig结构全是运行时有关的配置项。也即所有支持的配置项全在这个结构中了,后面我们在文件中配合的,都需要设置到这个配置对象中才行。首先初始化的时候都会会给一个默认的值。

那么我们配置文件在哪里加载?看代码

func init() {
	BConfig = newBConfig()
	var err error
	if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil {
		panic(err)
	}
	workPath, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	var filename = "app.conf"
	if os.Getenv("BEEGO_RUNMODE") != "" {
		filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
	}
	appConfigPath = filepath.Join(workPath, "conf", filename)
	if !utils.FileExists(appConfigPath) {
		appConfigPath = filepath.Join(AppPath, "conf", filename)
		if !utils.FileExists(appConfigPath) {
			AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()}
			return
		}
	}
	if err = parseConfig(appConfigPath); err != nil {
		panic(err)
	}
}

在上面的代码中可以看到

先找运行时的路径下的conf/app.conf,如果找不到再找程序所在目录下的conf/app.conf,如果还找不到,那么就给一个空的对象。

如果上面两个路径找到文件了,继续解析。。。咱们继续看代码。

func parseConfig(appConfigPath string) (err error) {
	AppConfig, err = newAppConfig(appConfigProvider, appConfigPath)
	if err != nil {
		return err
	}
	return assignConfig(AppConfig)
}

继续。。。。

func assignConfig(ac config.Configer) error {
	for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} {
		assignSingleConfig(i, ac)
	}


    ....
	// set the run mode first
	if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" {
		BConfig.RunMode = envRunMode
	} else if runMode := ac.String("RunMode"); runMode != "" {
		BConfig.RunMode = runMode
	}
    ...
}

注意了,重点到了:这里函数前3行,将配置的结构体,都转为interface{}切片,然后遍历调用了assignSingleConfig,很明显,这个函数,就是要将配置文件中的内容ac,替换到全局配置对象BConfig中,这个函数是替换单个结构体中的各个字段,不能递归替换字段中的字段,所以将BConfig中的所有结构体都单独拿出来。

而这三行后面的其他代码是做啥?阅读代码可看到,函数中后我...省略的代码,也是解析BConfig中的配置项,那么为什么不在assignSingleConfig中一起解析?因为这些单独解析的配置项有自己的优先级,如上面单独拿出来的RunMode,首先从环境变量中获取该配置,如果没有菜从配置文件中获取。

那么,现在我们继续探索assignSingleConfig函数

func assignSingleConfig(p interface{}, ac config.Configer) {
    // 获取type
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
    // 因为是指针,所以需要获取结构体
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}

    // 获取value
	pv := reflect.ValueOf(p).Elem()

    // 获取字段
	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		name := pt.Field(i).Name

        // 根据字段类型获取配置文件中的对应的配置项
		switch pf.Kind() {
		case reflect.String:
			pf.SetString(ac.DefaultString(name, pf.String()))
		case reflect.Int, reflect.Int64:
			pf.SetInt(ac.DefaultInt64(name, pf.Int()))
		case reflect.Bool:
			pf.SetBool(ac.DefaultBool(name, pf.Bool()))
		case reflect.Struct:
		default:
			//do nothing here
		}
	}

}

这下可就明白了!

三:config模块源码解析

config采用工厂模式来实现的,

config.go文件中定义了一个接口,然后其他文件中来实现这个接口。并通过函数

func Register(name string, adapter Config) 注册到其中。

最后我们可直接通过

func NewConfig(adapterName, filename string) (Configer, error)

函数来直接实例化一个对象。

然后直接使用这定义的接口中的方法。

首先看文件

选择同名文件config.go进入,可看到定义了一个接口

type Configer interface {
	Set(key, val string) error   //support section::key type in given key when using ini type.
	String(key string) string    //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
	Strings(key string) []string //get string slice
	Int(key string) (int, error)
	Int64(key string) (int64, error)
	Bool(key string) (bool, error)
	Float(key string) (float64, error)
	DefaultString(key string, defaultVal string) string      // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
	DefaultStrings(key string, defaultVal []string) []string //get string slice
	DefaultInt(key string, defaultVal int) int
	DefaultInt64(key string, defaultVal int64) int64
	DefaultBool(key string, defaultVal bool) bool
	DefaultFloat(key string, defaultVal float64) float64
	DIY(key string) (interface{}, error)
	GetSection(section string) (map[string]string, error)
	SaveConfigFile(filename string) error
}

注册到其中的方法:其实就是维护一个map而已

看其他实现的结构,使用该函数注册到其中

ini的

 

json的

其他的也都不用看了,非常简单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值