import (
創(chuàng)新互聯(lián)建站主營(yíng)榆社網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,app軟件開(kāi)發(fā)公司,榆社h5微信小程序開(kāi)發(fā)搭建,榆社網(wǎng)站營(yíng)銷(xiāo)推廣歡迎榆社等地區(qū)企業(yè)咨詢(xún)
"fmt"
"reflect"
)
func reflecType(x interface{}){
v := reflect.TypeOf(x)
fmt.Println("type:%v\n", v)
fmt.Println("type name:%v , rtpe kind:%v \n", v.getName(), v.getType())
}
type Cat struct{}
//通過(guò)反射設(shè)置變量的值
func reflectSetValue1(x interface{}){
v := reflect.ValueOf(x)
if v.Kind() == reflect.Int64{
v.SetInt(200) //修改的是副本, reflect 包會(huì)引發(fā)panic
}
}
//通過(guò)反射設(shè)置變量的值
func reflectSetValue2(x interface{}){
v := reflect.ValueOf(x)
//反射中使用Elem()獲取指針對(duì)應(yīng)的值
if v.Elem().Kind() == reflect.Int64{
v.Elem().SetInt(200)
}
}
func main(){
var a float32 = 3.14
reflectType(a) //type name:float32 type kind:float32
var b int64 = 100
reflectType(b) // type name :int64 type kind :int64
var c = Cat{}
reflectType(c) // type name :Cat type kind :struct
reflectSetValue1(b)
fmt.Println(b) //依然為100
reflectSetValue2(b)
}
維基百科中反射的定義:在計(jì)算機(jī)科學(xué)中,反射是指計(jì)算機(jī)程序在運(yùn)行時(shí)(Run time)可以訪問(wèn)、檢測(cè)和修改它本身狀態(tài)或行為的一種能力。用比喻來(lái)說(shuō),反射就是程序在運(yùn)行的時(shí)候能夠“觀察”并且修改自己的行為。
golang reflect包實(shí)現(xiàn)了反射。動(dòng)態(tài)的獲得程序運(yùn)行時(shí)對(duì)象的結(jié)構(gòu)和信息。
reflect 包中提供了兩個(gè)基礎(chǔ)的關(guān)于反射的函數(shù)來(lái)獲取上述的接口和結(jié)構(gòu)體:
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
大體上可以這樣理解,TypeOf獲取對(duì)象的類(lèi)型信息,ValueOf獲取對(duì)象中存儲(chǔ)的值。
golang tag
golang中可以為結(jié)構(gòu)體的字段添加tag。golang本身的encoding/json包解析json使用了tag,一些開(kāi)源的orm框架如gorm,也使用了tag。tag可以方便的為結(jié)構(gòu)體的字段添加一些信息,用reflect可以讀取到,加以利用。
這是一個(gè)用tag標(biāo)記列名以實(shí)現(xiàn)結(jié)構(gòu)體自動(dòng)生成xlsx的例子:
```
type Employee struct{
ID int `xlsx:”工號(hào)”`
Name string `xlsx:”姓名”`
Email string `xlsx:”郵箱”`
}
func Outputxlsx(es []*Employee) ([]byte, error) {
xt := reflect.TypeOf(es[0])
xv := reflect.ValueOf(es[0])
rows := [][]interface{}{}
headers := []interface{}{}
for i := 0; i xt.Elem().NumField(); i++ {
head, ok := xt.Elem().Field(i).Tag.Lookup("xlsx")
if ok {
headers = append(headers, head)
}
}
for _, e := range es {
cells := []interface{}{}
xv := reflect.ValueOf(e)
for i := 0; i xv.Elem().NumField(); i++ {
_, ok := xt.Elem().Field(i).Tag.Lookup("xlsx")
if ok {
cells = append(cells, xv.Elem().Field(i).Interface())
}
}
rows = append(rows, cells)
}
file := xlsx.NewFile()
sheet, _ := file.AddSheet("sheet1")
row := sheet.AddRow()
for _, header := range headers {
row.AddCell().Value = fmt.Sprintf("%v", header)
}
for _, v := range rows {
row := sheet.AddRow()
for _, vv := range v {
row.AddCell().Value = fmt.Sprintf("%v", vv)
}
}
var buffer bytes.Buffer
if err := file.Write(buffer); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
```
Fx是一個(gè)golang版本的依賴(lài)注入框架,它使得golang通過(guò)可重用、可組合的模塊化來(lái)構(gòu)建golang應(yīng)用程序變得非常容易,可直接在項(xiàng)目中添加以下內(nèi)容即可體驗(yàn)Fx效果。
Fx是通過(guò)使用依賴(lài)注入的方式替換了全局通過(guò)手動(dòng)方式來(lái)連接不同函數(shù)調(diào)用的復(fù)雜度,也不同于其他的依賴(lài)注入方式,F(xiàn)x能夠像普通golang函數(shù)去使用,而不需要通過(guò)使用struct標(biāo)簽或內(nèi)嵌特定類(lèi)型。這樣使得Fx能夠在很多go的包中很好的使用。
接下來(lái)會(huì)提供一些Fx的簡(jiǎn)單demo,并說(shuō)明其中的一些定義。
1、一般步驟
大致的使用步驟就如下。下面會(huì)給出一些完整的demo
2、簡(jiǎn)單demo
將io.reader與具體實(shí)現(xiàn)類(lèi)關(guān)聯(lián)起來(lái)
輸出:
3、使用struct參數(shù)
前面的使用方式一旦需要進(jìn)行注入的類(lèi)型過(guò)多,可以通過(guò)struct參數(shù)方式來(lái)解決
輸出
如果通過(guò)Provide提供構(gòu)造函數(shù)是生成相同類(lèi)型會(huì)有什么問(wèn)題?換句話(huà)也就是相同類(lèi)型擁有多個(gè)值呢?
下面兩種方式就是來(lái)解決這樣的問(wèn)題。
4、使用struct參數(shù)+Name標(biāo)簽
在Fx未使用Name或Group標(biāo)簽時(shí)不允許存在多個(gè)相同類(lèi)型的構(gòu)造函數(shù),一旦存在會(huì)觸發(fā)panic。
輸出
上面通過(guò)Name標(biāo)簽即可完成在Fx容器注入相同類(lèi)型
5、使用struct參數(shù)+Group標(biāo)簽
使用group標(biāo)簽同樣也能完成上面的功能
輸出
基本上Fx簡(jiǎn)單應(yīng)用在上面的例子也做了簡(jiǎn)單講解
1、Annotated(位于annotated.go文件) 主要用于采用annotated的方式,提供Provide注入類(lèi)型
源碼中Name和Group兩個(gè)字段與前面提到的Name標(biāo)簽和Group標(biāo)簽是一樣的,只能選其一使用
2、App(位于app.go文件) 提供注入對(duì)象具體的容器、LiftCycle、容器的啟動(dòng)及停止、類(lèi)型變量及實(shí)現(xiàn)類(lèi)注入和兩者映射等操作
至于Provide和Populate的源碼相對(duì)比較簡(jiǎn)單易懂在這里不在描述
具體源碼
3、Extract(位于extract.go文件)
主要用于在application啟動(dòng)初始化過(guò)程通過(guò)依賴(lài)注入的方式將容器中的變量值來(lái)填充給定的struct,其中target必須是指向struct的指針,并且只能填充可導(dǎo)出的字段(golang只能通過(guò)反射修改可導(dǎo)出并且可尋址的字段),Extract將被Populate代替。 具體源碼
4、其他
諸如Populate是用來(lái)替換Extract的,而LiftCycle和inout.go涉及內(nèi)容比較多后續(xù)會(huì)單獨(dú)提供專(zhuān)屬文件說(shuō)明。
在Fx中提供的構(gòu)造函數(shù)都是惰性調(diào)用,可以通過(guò)invocations在application啟動(dòng)來(lái)完成一些必要的初始化工作:fx.Invoke(function); 通過(guò)也可以按需自定義實(shí)現(xiàn)LiftCycle的Hook對(duì)應(yīng)的OnStart和OnStop用來(lái)完成手動(dòng)啟動(dòng)容器和關(guān)閉,來(lái)滿(mǎn)足一些自己實(shí)際的業(yè)務(wù)需求。
Fx框架源碼解析
主要包括app.go、lifecycle.go、annotated.go、populate.go、inout.go、shutdown.go、extract.go(可以忽略,了解populate.go)以及輔助的internal中的fxlog、fxreflect、lifecycle
1、反射可以在運(yùn)行時(shí) 動(dòng)態(tài)獲取變量的各種信息 ,比如變量的類(lèi)型、類(lèi)別;
2、如果是結(jié)構(gòu)體變量,還可以獲取到結(jié)構(gòu)體本身的信息(包括結(jié)構(gòu)體的字段、方法);
3、通過(guò)反射,可以修改 變量的值 ,可以調(diào)用關(guān)聯(lián)的方法;
4、使用反射,需要import " reflect ".
5、示意圖:
1、不知道接口調(diào)用哪個(gè)函數(shù),根據(jù)傳入?yún)?shù)在運(yùn)行時(shí)確定調(diào)用的具體接口,這種需要對(duì)函數(shù)或方法反射。
例如以下這種橋接模式:
示例第一個(gè)參數(shù)funcPtr以接口的形式傳入函數(shù)指針,函數(shù)參數(shù)args以可變參數(shù)的形式傳入,bridge函數(shù)中可以用反射來(lái)動(dòng)態(tài)執(zhí)行funcPtr函數(shù)。
1、reflect.TypeOf(變量名),獲取變量的類(lèi)型,返回reflect.Type類(lèi)型。
2、reflect.ValueOf(變量名),獲取變量的值,返回reflect.Value類(lèi)型reflect.Value是一個(gè)結(jié)構(gòu)體類(lèi)型。
3、變量、interface{}和reflect.Value是可以互相轉(zhuǎn)換的,這點(diǎn)在實(shí)際開(kāi)發(fā)中,會(huì)經(jīng)常使用到。
1、reflect.Value.Kind,獲取變量的 類(lèi)別(Kind) ,返回的是一個(gè) 常量 。在go語(yǔ)言文檔中:
示例如下所示:
輸出如下:
Kind的范疇要比Type大。比如有Student和Consumer兩個(gè)結(jié)構(gòu)體,他們的 Type 分別是 Student 和 Consumer ,但是它們的 Kind 都是 struct 。
2、Type是類(lèi)型,Kind是類(lèi)別,Type和Kind可能是相同的,也可能是不同的。
3、通過(guò)反射可以在讓 變量 在 interface{} 和 Reflect.Value 之間相互轉(zhuǎn)換,這點(diǎn)在前面畫(huà)過(guò)示意圖。
4、使用反射的方式來(lái)獲取變量的值(并返回對(duì)應(yīng)的類(lèi)型),要求數(shù)據(jù)類(lèi)型匹配,比如x是int,那么久應(yīng)該使用reflect.Value(x).Int(),而不能使用其它的,否則報(bào)panic。
如果是x是float類(lèi)型的話(huà),也是要用reflect.Value(x).Float()。但是如果是struct類(lèi)型的話(huà),由于type并不確定,所以沒(méi)有相應(yīng)的方法,只能 斷言。
5、通過(guò)反射的來(lái)修改變量,注意當(dāng)使用SetXxx方法來(lái)設(shè)置需要通過(guò)對(duì)應(yīng)的指針類(lèi)型來(lái)完成,這樣才能改變傳入的變量的值,同時(shí)需要使用到reflect.Value.Elem()方法。
輸出num=20,即成功使用反射來(lái)修改傳進(jìn)來(lái)變量的值。
6、reflect.Value.Elem()應(yīng)該如何理解?