介紹
我們知道反射是可以在程序的運行期間獲取到變量或者結構體的一些元信息桥爽,能夠知道類型杠览,變量的值甚至修改值丰辣,執(zhí)行方法等
java中的反射應用非常廣泛写烤,基本上所有框架都用到了動態(tài)代理翼闽,而動態(tài)代理就是基于反射實現的。
在go中也專門提供了一個標準庫reflect洲炊,下面我們就基于這個庫來簡單使用下感局。
我們準備一個結構體Person,包含有屬性方法暂衡。
1 創(chuàng)建該結構體的一個對象询微,通過反射獲取到該對象的 結構體名稱、類型狂巢、各個字段的名字撑毛、各個字段的tag、獲取方法并執(zhí)行方法唧领。
2 通過反射構建一個該結構體實例藻雌,給每個字段填充值。
package main
import (
"fmt"
"reflect"
"time"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Married bool `json:"married"`
BirthTime time.Time `json:"birthTime"`
}
func (p *Person) Eat(name string) {
fmt.Println("執(zhí)行:吃", name)
}
func main() {
location, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return
}
//解析時間
parse, err := time.ParseInLocation("2006-01-02 15:04:05", "1998-10-04 21:00:32", location)
// 1 構建一個person指針對象
person := &Person{Name: "wendell", Age: 20, Married: false, BirthTime: parse}
// 拿到類型
t := reflect.TypeOf(person)
// 拿到value
v := reflect.ValueOf(person)
fmt.Printf("type:%s\nvalue:%v\n", t, v)
// 判斷是否是指針類型斩个,一般反射我們都習慣操作指針
fmt.Println("是否是指針類型", t.Kind() == reflect.Pointer)
// 只有是指針或者interface才能調用該方法胯杭,返回具體的值
// 如果說一開始操作的不是指針,那么這一步可以省略受啥,直接使用 t v 即可
tElem := t.Elem()
vElem := v.Elem()
fmt.Println("對象名字:", tElem.Name())
fmt.Println("字段個數:", tElem.NumField())
for i := 0; i < tElem.NumField(); i++ {
fmt.Printf("[%v]\t[%v]\t[%v]\t[%v]\n", tElem.Field(i).Name, tElem.Field(i).Tag, tElem.Field(i).Tag.Get("json"), vElem.Field(i).Interface())
}
// 獲取到方法做个,執(zhí)行方法
method := v.MethodByName("Eat")
args := make([]reflect.Value, 1)
args[0] = reflect.ValueOf("蘋果")
method.Call(args)
fmt.Println("------------------------------------------")
// 2 創(chuàng)建一個新的person對象
value := reflect.New(t.Elem()).Interface()
fmt.Println("構建新的對象", value)
valueOf := reflect.ValueOf(value)
elem := valueOf.Elem()
name := reflect.ValueOf("juan")
age := reflect.ValueOf(25)
fmt.Println("name是否允許重新設置值:", elem.Field(0).CanSet())
// 通過反射給對象設置值
elem.Field(0).Set(name)
fmt.Println("age是否允許重新設置值:", elem.Field(1).CanSet())
elem.Field(1).Set(age)
fmt.Println("反射設置值之后的", value)
}
執(zhí)行結果
type:*main.Person
value:&{wendell 20 false 1998-10-04 21:00:32 +0800 CST}
是否是指針類型 true
對象名字: Person
字段個數: 4
[Name] [json:"name"] [name] [wendell]
[Age] [json:"age"] [age] [20]
[Married] [json:"married"] [married] [false]
[BirthTime] [json:"birthTime"] [birthTime] [1998-10-04 21:00:32 +0800 CST]
執(zhí)行:吃 蘋果
------------------------------------------
構建新的對象 &{ 0 false 0001-01-01 00:00:00 +0000 UTC}
name是否允許重新設置值: true
age是否允許重新設置值: true
反射設置值之后的 &{juan 25 false 0001-01-01 00:00:00 +0000 UTC}
Process finished with the exit code 0
通過上面例子我們大概就清楚反射給我們提供了一些什么功能。那么實際框架中怎么應用呢滚局。加入你要做一個orm框架居暖。大概要做到如下程度。只需要打開一個session給出查詢條件執(zhí)行查詢就能返回需要的值藤肢,并且該值已經封裝成了user集合
u := &User{}
userList, err := DB.OpenSession().Like("user_id", "test").SelectAll(u)
那么我們都知道執(zhí)行一條查詢語句需要知道 表名太闺,字段名那么上面代碼都沒有設置如何獲取,這個時候我就可以形成一種規(guī)范嘁圈,結構體名字就是默認表名跟束,屬性名稱的下劃線形式就是默認字段名可以通過tag指定字段名叫啥莺奸。返回值我們可以通過傳入的類型New出一個結構體對象,通過該對象的屬性與表字段名的映射關系就可以自動將查詢出的結果設置到結構體中冀宴,
于是結構體可以這樣寫
type User struct {
UserId string `db:"user_id",primary:"true"`
Username string `db:"user_name"`
Admin bool
}
對應的sql語句
select user_id,username,admin from user_id like "%test%"
好了灭贷,反射的基本使用就到這里,后面會發(fā)布orm框架的簡單實現略贮。
歡迎關注甚疟,學習不迷路!