深入了解Golang中reflect反射的使用

2023-05-20 05:05:03 反射 Golang reflect

1. 介绍

在反射的世界里,我们拥有了获取一个对象的类型,属性及方法的能力。

Go 反射的世界里,有两种类型非常重要,是整个反射的核心,在学习 reflect 包的使用时,先得学习下这两种类型:

  • reflect.Type
  • reflect.Value

2. 方法示例

2.1 通过反射获取对象的键(类型)和值

package main
import (
   "fmt"
   "reflect"
)
//
func reflectNum(arg interface{})  {
   fmt.Println("type:",reflect.TypeOf(arg))
   fmt.Println("value : ",reflect.ValueOf(arg))
}
func main() {
   reflectNum(1.1234)
}

type: float64
value : 1.1234

注意,Go 中所有对象都是键+值组成的,含有这种特性的对象都是可以用 Type + Value 进行抽象的,这就是万能对象

2.2 反射对象的类型和属性

package main
import (
   "fmt"
   "reflect"
)
type User struct {
   Id int
   Name string
   Age int
}
func (this User) Call()  {
   fmt.Println("user is called ..")
   fmt.Printf("#{this}\n")
}
func main() {
   user := User{1,"Aceld",18}
   //传入 user 对象
   DoFiledAndMethod(user)
}
//接收万能对象 用反射解析
func DoFiledAndMethod(input interface{})  {
   //反射获取类的类型
   inputType := reflect.TypeOf(input)
   fmt.Println("inputType is :", inputType.Name() )
  // inputType is : User
   inputValue := reflect.ValueOf(input)
   fmt.Println("inputValue is :", inputValue)
  //inputValue is : {1 Aceld 18}
   //反射获取类的属性
   for i := 0; i < inputType.NumField(); i++ {
      field := inputType.Field(i)
      value := inputValue.Field(i).Interface()
      fmt.Printf("%s : %v = %v\n",field.Name,field.Type,value)
      //Id : int = 1
      //Name : string = Aceld
      //Age : int = 18
   }
   //反射调用方法
   for i := 0; i < inputType.NumMethod(); i++ {
      m := inputType.Method(i)
      //%s 输出字符串表示(string类型或[]byte)
      //%v  相应值的默认格式。
      fmt.Printf("%s:%v\n",m.Name,m.Type)
     //Call:func(main.User)
   }
}

3. 反射对Json的操作

3.1 反射与Json属性解析

在结构体属性中 我们可以通过加 tag 以供其他程序的解析与识别

package main
import (
   "fmt"
   "reflect"
)
type resume struct {
   // ':'前后不加空格  ` != '
   Name string `info:"name"  doc:"我的名字" `
   Sex string `info:"sex" `
}
func findTag(str interface{})  {
   // Elem returns a type's element type.
   t := reflect.TypeOf(str).Elem()
  //遍历接收对象的属性
   for i := 0; i < t.NumField(); i++ {
      taginfo := t.Field(i).Tag.Get("info")
      tagdoc := t.Field(i).Tag.Get("doc")
      fmt.Println("info:",taginfo,"doc:",tagdoc)
   }
}
func main() {
   var re resume
   findTag(&re)
}
输出:
  info: name doc: 我的名字
  info: sex doc:

3.2 Json包的序列化与反序列化

go 的 json 工具要想解析 需要你在字段上添加 json tag

package main
import (
   "encoding/json"
   "fmt"
)
// 全部添加 json 的 tag
type Movie struct {
   Title string `json:"title"`
   Year int `json:"year"`
   Price int `json:"rmb"`
   Actors []string `json:"actors"`
}
func main() {
   movie := Movie{"喜剧之王", 2000, 10, []string{"zhouxinGChi", "wumengda"}}
   // 序列化
   jsonStr, err := json.Marshal(movie)
   if err != nil {
      fmt.Println("json marshal error",err)
      return
   }
   fmt.Printf("jsonStr = %s\n",jsonStr)
   //反序列化
   m := Movie{}
   err = json.Unmarshal(jsonStr, &m)
   if err != nil {
      fmt.Println("json ummarshal error", err)
      return
   }
   fmt.Printf("%v\n",m)
}

4. 实战巩固

4.1 需求

项目经常会在启动的时候会加一些环境变量参数,随着启动而触发作用加载到项目中,现如今在。利用os包设置好环境变量,利用反射实现项目启动时运行时加载进环境!

环境变量KeyCONFIG_SERVER_NAMECONFIG_SERVER_IPCONFIG_SERVER_URL

4.2 代码实现

package main
import (
	"fmt"
	"os"
	"reflect"
	"strings"
)
type Config struct {
	Name    string `json:"server-name"`
	IP      string `json:"server-ip"`
	URL     string `json:"server-url"`
	Timeout string `json:"timeout"`
}
func readConfig() *Config {
	// read from xxx.json,省略
	config := Config{}
	// 1. 拿到config的反射类型
	typ := reflect.TypeOf(config)
	// 2. 拿到config的反射值对象
	value := reflect.Indirect(reflect.ValueOf(&config))
	// 3. 遍历每个字段
	for i := 0; i < typ.NumField(); i++ {
		// 3.1 拿到反射的config的第i个字段
		f := typ.Field(i)
		// 3.2 拿到赋值了json的字段tag
		if v, ok := f.Tag.Lookup("json"); ok {
			// 3.3 拼接系统环境变量Key
			key := fmt.Sprintf("CONFIG_%s", strings.ReplaceAll(strings.ToUpper(v), "-", "_"))
			// 3.4 从系统中读取环境变量对应的值
			if env, exist := os.LookupEnv(key); exist {
				// 3.5 将值设置到给定名称的字段上
				value.FieldByName(f.Name).Set(reflect.ValueOf(env))
			}
		}
	}
	return &config
}
func main() {
	os.Setenv("CONFIG_SERVER_NAME", "global_server")
	os.Setenv("CONFIG_SERVER_IP", "10.0.0.1")
	os.Setenv("CONFIG_SERVER_URL", "geektutu.com")
	c := readConfig()
	fmt.Printf("%+v", c)
}

到此这篇关于深入了解golang中reflect反射的使用的文章就介绍到这了,更多相关Go reflect反射内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章