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 errorif 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 nameRunMode string //Running Mode: dev | prodRouterCaseSensitive boolServerName stringRecoverPanic boolRecoverFunc func(*context.Context)CopyRequestBody boolEnableGzip boolMaxMemory int64EnableErrorsShow boolEnableErrorsRender boolListen ListenWebConfig WebConfigLog LogConfig
}
type Listen struct {Graceful bool // Graceful means use graceful module to start the serverServerTimeOut int64ListenTCP4 boolEnableHTTP boolHTTPAddr stringHTTPPort intAutoTLS boolDomains []stringTLSCacheDir stringEnableHTTPS boolEnableMutualHTTPS boolHTTPSAddr stringHTTPSPort intHTTPSCertFile stringHTTPSKeyFile stringTrustCaFile stringEnableAdmin boolAdminAddr stringAdminPort intEnableFcgi boolEnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O
}// WebConfig holds web related config
type WebConfig struct {AutoRender boolEnableDocs boolFlashName stringFlashSeparator stringDirectoryIndex boolStaticDir map[string]stringStaticExtensionsToGzip []stringTemplateLeft stringTemplateRight stringViewsPath stringEnableXSRF boolXSRFKey stringXSRFExpire intSession SessionConfig
}// SessionConfig holds session related config
type SessionConfig struct {SessionOn boolSessionProvider stringSessionName stringSessionGCMaxLifetime int64SessionProviderConfig stringSessionCookieLifeTime intSessionAutoSetCookie boolSessionDomain stringSessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies.SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headersSessionNameInHTTPHeader stringSessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
}// LogConfig holds Log related config
type LogConfig struct {AccessLogs boolEnableStaticLogs bool //log static files requests default: falseAccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty stringFileLineNum boolOutputs map[string]string // Store Adaptor : config
}
BConfig结构全是运行时有关的配置项。也即所有支持的配置项全在这个结构中了,后面我们在文件中配合的,都需要设置到这个配置对象中才行。首先初始化的时候都会会给一个默认的值。
那么我们配置文件在哪里加载?看代码
func init() {BConfig = newBConfig()var err errorif 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 firstif 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) {// 获取typept := reflect.TypeOf(p)if pt.Kind() != reflect.Ptr {return}// 因为是指针,所以需要获取结构体pt = pt.Elem()if pt.Kind() != reflect.Struct {return}// 获取valuepv := 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 sliceInt(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 sliceDefaultInt(key string, defaultVal int) intDefaultInt64(key string, defaultVal int64) int64DefaultBool(key string, defaultVal bool) boolDefaultFloat(key string, defaultVal float64) float64DIY(key string) (interface{}, error)GetSection(section string) (map[string]string, error)SaveConfigFile(filename string) error
}
注册到其中的方法:其实就是维护一个map而已
看其他实现的结构,使用该函数注册到其中
ini的
json的
其他的也都不用看了,非常简单
标签:
相关文章
-
无相关信息