go語言反射探秘
以下分為兩個部分講講go中的反射:
第一部分是核心要點
TypeOf
和
ValueOf
第二部分是應用反射功能寫一個簡單DI依賴注入容器
1
TypeOf
和
ValueOf
TypeOf
返回
Type
介面,
ValueOf
返回
Value
結構體,此二者是後續所有操作的核心
TypeOf
reflect。TypeOf()
返回的是一個
Type
介面,定義了大多數對型別元資料的操作,比如
String()
是獲取型別名,
Kind()
返回具體型別的列舉值,
NumIn()
為傳入的是當函式型別時返回所有傳入引數的數量,同理
NumOut()
返回的是函式返回值的數量。
由此我們可以推測底層必然有一個
type結構體
實現了這個介面。
// Type介面原型: 我們需要終點關注的String() Elem() Kind()
type
Type
interface
{
// ……。
// String returns a string representation of the type。
// The string representation may use shortened package names
// (e。g。, base64 instead of “encoding/base64”) and is not
// guaranteed to be unique among types。 To test for type identity,
// compare the Types directly。
String
()
string
// Kind returns the specific kind of this type。
Kind
()
Kind
// Elem returns a type‘s element type。
// It panics if the type’s Kind is not Array, Chan, Map, Ptr, or Slice。
Elem
()
Type
// In returns the type of a function type‘s i’th input parameter。
// It panics if the type‘s Kind is not Func。
// It panics if i is not in the range [0, NumIn())。
In
(
i
int
)
Type
// Key returns a map type’s key type。
// It panics if the type‘s Kind is not Map。
Key
()
Type
// Len returns an array type’s length。
// It panics if the type‘s Kind is not Array。
Len
()
int
// NumField returns a struct type’s field count。
// It panics if the type‘s Kind is not Struct。
NumField
()
int
// NumIn returns a function type’s input parameter count。
// It panics if the type‘s Kind is not Func。
NumIn
()
int
// NumOut returns a function type’s output parameter count。
// It panics if the type‘s Kind is not Func。
NumOut
()
int
// Implements reports whether the type implements the interface type u。
Implements
(
u
Type
)
bool
// ……。
}
假設我們在
package main
的包中定義了一個結構體:
type actorix struct
然後main方法中呼叫
reflect。TypeOf()
,然後列印其型別,會輸出
main。actorix
package
main
type
actorix
struct
{
id
int
name
string
number
float64
}
func
main
()
{
var
a
=
actorix
{
id
:
1
,
name
:
“this”
,
number
:
3。14
}
var
atype
=
reflect
。
TypeOf
(
a
)
fmt
。
Println
(
“typeof a is: ”
,
atype
。
String
())
// 列印結果:
// typeof a is: main。actorix
}
接下來我們具體看看
TypeOf
是怎麼工作的:
// TypeOf函式原型
func
TypeOf
(
i
interface
{})
Type
{
// 先把runtime。eface型別轉化為emptyInterface
// runtime。eface型別和emptyInterface型別本質是一樣的
// 這樣以來此時的emptyInterface。typ型別為*rtype
eface
:=
*
(
*
emptyInterface
)(
unsafe
。
Pointer
(
&
i
))
// 然後用toType()函式將emptyInterface。typ包裝成Type型別的返回值
// 到這裡我們可以明確rtype肯定實現了Type介面
return
toType
(
eface
。
typ
)
}
// emptyInterface結構原型
type
emptyInterface
struct
{
typ
*
rtype
// 指向的型別元資料
word
unsafe
。
Pointer
// 資料的指標
}
// eface結構體原型
type
eface
struct
{
_type
*
_type
// 指向的型別元資料
data
unsafe
。
Pointer
// 資料的指標
}
func
toType
(
t
*
rtype
)
Type
{
if
t
==
nil
{
return
nil
}
return
t
}
函式呼叫鏈如下:
TypeOf(interface{}) -> toType(*type)
接下來我們再來看看
rtype
結構體,可見它就是我們的型別元資料資訊,包括型別、大小、對其方式、雜湊值等,在type。go的原始碼中
rtype
實現了
Type
介面的方法,我們傳入的動態型別都指向了這個型別元資料
// rtype must be kept in sync with 。。/runtime/type。go:/^type。_type。
type
rtype
struct
{
size
uintptr
ptrdata
uintptr
// number of bytes in the type that can contain pointers
hash
uint32
// hash of type; avoids computation in hash tables
tflag
tflag
// extra type information flags
align
uint8
// alignment of variable with this type
fieldAlign
uint8
// alignment of struct field with this type
kind
uint8
// enumeration for C
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal
func
(
unsafe
。
Pointer
,
unsafe
。
Pointer
)
bool
gcdata
*
byte
// garbage collection data
str
nameOff
// string form
ptrToThis
typeOff
// type for pointer to this type, may be zero
}
ValueOf
ValueOf
返回一個
Value
結構體,我們其實可以將其理解為:對我們傳入的變數進行了重新包裝,將它的資料指標、型別元資料、識別符號一併打包在一個結構體中返回,供我們後續使用:
type
Value
struct
{
typ
*
rtype
// 反射變數的型別元資料指標
ptr
unsafe
。
Pointer
// 資料地址
flag
// 位識別符號:描述資訊,是否為指標,是否為方法等,是否只讀等
}
// ValueOf函式原型
func
ValueOf
(
i
interface
{})
Value
{
if
i
==
nil
{
return
Value
{}
}
// TODO: Maybe allow contents of a Value to live on the stack。
// For now we make the contents always escape to the heap。 It
// makes life easier in a few places (see chanrecv/mapassign
// comment below)。
// 官方再這個函式中已經明確說明將引數i逃逸到堆上
escapes
(
i
)
return
unpackEface
(
i
)
}
// unpackEface 函式本質上做了三件事情:
// 1轉換為將runtime。eface型別的i轉換為emptyInterface
// 2判明e。typ是否為空,如果為空返回一個空Value結構體
// 3如果不為空,根據e。typ的型別明確要返回的flag識別符號
// 最後將三者聯合在一起作為Value結構體返回
func
unpackEface
(
i
interface
{})
Value
{
e
:=
(
*
emptyInterface
)(
unsafe
。
Pointer
(
&
i
))
// NOTE: don’t read e。word until we know whether it is really a pointer or not。
t
:=
e
。
typ
if
t
==
nil
{
return
Value
{}
}
f
:=
flag
(
t
。
Kind
())
if
ifaceIndir
(
t
)
{
f
|=
flagIndir
}
return
Value
{
t
,
e
。
word
,
f
}
}
從
ValueOf
的函式原型中我們可以看到其傳入的函式引數是一個
interface{}
的空型別。
然而最後會將傳入的引數逃逸到堆上,所以我們要注意如下一個問題:
package
main
func
main
()
{
var
s
string
=
“this is sparta”
// 從編譯角度看,這裡可以分為兩步:
// 1 是臨時建立一個s的複製
// 2 s的複製作為引數傳入ValueOf後,被顯式地逃逸到了堆上
// 3 返回值中svalue指向的ptr,是堆上的那個s的地址,並非我們原先定義的s
var
svalue
=
reflect
。
ValueOf
(
s
)
// 4 當我們直接用SetString修改堆上的s複製沒有任何意義panic
svalue
。
SetString
(
“this is not sparta”
)
}
// 列印結果
// panic: reflect: reflect。Value。SetString using unaddressable value
// SetString函式原型
// SetString sets v‘s underlying value to x。
// It panics if v’s Kind is not String or if CanSet() is false。
func
(
v
Value
)
SetString
(
x
string
)
{
v
。
mustBeAssignable
()
// 確認必須可以被賦值
v
。
mustBe
(
String
)
// 確認必須是String型別
*
(
*
string
)(
v
。
ptr
)
=
x
// 直接指標操作賦值
}
這裡我們如果要完成原本操作需要藉助
Elem()
func
main
(){
var
s
string
=
“this is sparta”
// 注意這裡要傳遞s的指標
// 仍然氛圍兩部分引數複製了一個s的地址進入ValueOf
// 仍然將其逃逸到了堆上,此時svalue返回的是Value。ptr = 存放s地址的堆上的地址
// 比如s的地址為0x01,到了堆上,堆地址為:0xA,存放資料為0x01, Value。ptr = 0xA
// 相當於一個二級指標,因此直接SetString是沒有用的仍然是panic
// 需要透過Elem()函式 => 關鍵程式碼: ptr = *(*unsafe。Pointer)(ptr)
// 返回其指標指向的值,相當於二級指標解引用為一級指標
// 如此一來 svalue = svalue。Elem()後, svalue就是&s
// 我們可以對其進行SetString修改
var
svalue
=
reflect
。
ValueOf
(
&
s
)
svalue
=
svalue
。
Elem
()
svalue
。
SetString
(
“this is not sparta”
)
fmt
。
Println
(
s
)
}
// 列印結果:
// this is not sparta
// Elem函式原型如下
func
(
v
Value
)
Elem
()
Value
{
k
:=
v
。
kind
()
switch
k
{
case
Interface
:
var
eface
interface
{}
if
v
。
typ
。
NumMethod
()
==
0
{
eface
=
*
(
*
interface
{})(
v
。
ptr
)
}
else
{
eface
=
(
interface
{})(
*
(
*
interface
{
M
()})(
v
。
ptr
))
}
x
:=
unpackEface
(
eface
)
if
x
。
flag
!=
0
{
x
。
flag
|=
v
。
flag
。
ro
()
}
return
x
case
Ptr
:
ptr
:=
v
。
ptr
if
v
。
flag
&
flagIndir
!=
0
{
ptr
=
*
(
*
unsafe
。
Pointer
)(
ptr
)
}
// The returned value‘s address is v’s value。
if
ptr
==
nil
{
return
Value
{}
}
tt
:=
(
*
ptrType
)(
unsafe
。
Pointer
(
v
。
typ
))
typ
:=
tt
。
elem
fl
:=
v
。
flag
&
flagRO
|
flagIndir
|
flagAddr
fl
|=
flag
(
typ
。
Kind
())
return
Value
{
typ
,
ptr
,
fl
}
}
panic
(
&
ValueError
{
“reflect。Value。Elem”
,
v
。
kind
()})
}
如何驗證Elem()的作用?
package
main
// 仍然定義一個結構體
type
actorix
struct
{
id
int
name
string
number
float64
}
func
main
()
{
// 初始化一個actorix
var
a
=
actorix
{
id
:
3
,
name
:
“vs”
,
number
:
8。99
}
// 注意這裡很關鍵我們傳入的是&a
var
atype
=
reflect
。
ValueOf
(
&
a
)
fmt
。
Println
(
“kind of atype is: ”
,
atype
。
Kind
())
atype
=
atype
。
Elem
()
fmt
。
Println
(
“kind of atype。Elem() is: ”
,
atype
。
Kind
())
fmt
。
Println
(
“value in atype is: ”
,
atype
)
}
// 列印結果:
// kind of atype is: ptr
// kind of atype。Elem() is: struct
// value in atype is: {3 vs 8。99}
如果我們把
var atype = reflect。ValueOf(&a)
改為
var atype = reflect。ValueOf(a)
func
main
()
{
var
a
=
actorix
{
id
:
3
,
name
:
“vs”
,
number
:
8。99
}
var
atype
=
reflect
。
ValueOf
(
a
)
fmt
。
Println
(
“kind of atype is: ”
,
atype
。
Kind
())
// 此時要注意這裡的atype和a不是一回事,指向的地址不一樣,atype是a在堆中的複製
// 這裡編譯器會阻止一切修改:
// 如果新增程式碼: atype。FieldByName(“id”)。SetInt(9) ,就會報錯
fmt
。
Println
(
“value in atype is: ”
,
atype
)
}
// 列印結果:
// kind of atype is: struct
// value in atype is: {3 vs 8。99}
2 應用反射功能寫一個簡單DI依賴注入容器
首先我們來簡單講一下什麼是依賴注入。舉個不恰當的例子:
某位老闆想要做假賬騙錢,於是對財務總監說:你把賬調整一下,財務總監心領神會於是對手下的老財說:你把賬調整一下,老闆等著要!
很顯然上述過程中,老闆最終需要的是老財調整好的賬本,但他不會直接指揮老財,而是下達命令給財務總監。老闆本人是不會做賬,他等著財務總監給他反饋;而財務總監也是不做賬的,他等著老財給他反饋。
老闆想要的結果
依賴於
財務總監的執行情況,財務總監想要的結果
依賴於
老財的執行結果。
這就是依賴注入想要解決的問題,在現實大型程式中,任務鏈可能非常長,如果企業層層上報的體制一樣,一個類的實現可能依賴於很多呼叫鏈上的其他類的實現,但我們只想做老闆,只關心本層的實現,並不想關心下一層會怎麼做,就像我們例子中老闆並不在意這件調賬這項工作到底是財務總監做還是財務總監手下的老財做,所以我們需要一個機制,幫助我們一旦發出呼叫某個類的指令,就會事先自動呼叫這個類的實現,這同樣可以理解為控制反轉。
我們先來做一個最簡單的基礎原型,為了簡便起見我們只設定兩層依賴(從財務總監發出order到老財),然後把依賴注入全部放到一個
diInvoke()
函式當中:
// Accountant 我們先設計一個老財結構體
type
Accountant
struct
{
name
string
}
// ctorAccountant 初始化Accountant結構體的函式,其返回值為Accountant
func
ctorAccountant
()
Accountant
{
return
Accountant
{
name
:
“十年老會計”
}
}
// order 命令函式,傳入引數是Accountant,我們需要一個指揮老財幫我們做賬
// order的呼叫依賴於Accontant結構體的構建
func
order
(
a
Accountant
)
{
fmt
。
Println
(
“需要去做假賬的老財是: ”
,
a
。
name
)
}
// 這是我們的依賴注入函式,我們預期的效果是,他傳入一個函式型別的引數(也就是order函式)
// 然後會自動解析order的形參(也就是Accountant結構體)
// 根據這個形參型別自動去一張dimap中尋找返回值為該形參型別的函式(也就是ctorAccountant函式)
// 然後呼叫該函式,並將其結果儲存在一張形引數組中,此時order函式所需要的形參已經構建完畢
// 最後呼叫order函式本身
// 完畢
func
diInvoke
(
function
interface
{})
error
{
// 新建一張dimap我們要將ctorAccountant函式註冊進去
// dimap的key是ctorAccountant返回值的reflect。Type
// dimap的value是ctorAccountant本身的reflect。Value(也可以理解為reflect。Value結構體模式的函式指標)
var
dimap
=
make
(
map
[
reflect
。
Type
]
reflect
。
Value
)
var
funcs
=
ctorAccountant
// 透過ValueOf函式我們將ctorAccountant轉換為Value結構體
var
t
=
reflect
。
ValueOf
(
funcs
)
// vt儲存ctorAccountant的Type型別
var
vt
=
t
。
Type
()
// 我們這裡使用vt。NumOut()獲取所有ctorAccountant函式的返回值數量
// 並用vt。Out()函式將所有返回值型別儲存再一個results陣列中(Type。Out()返回值型別為reflect。Type)
var
results
=
make
([]
reflect
。
Type
,
vt
。
NumOut
())
for
i
:=
0
;
i
<
vt
。
NumOut
();
i
++
{
// Out函式原型: Out func(i int) Type 返回第i個型別變數的返回值
results
[
i
]
=
vt
。
Out
(
i
)
}
// 同理這裡處理的是形參,但與返回值的處理不同,形參我們不能儲存為reflect。Type的陣列
// 需要儲存為reflect。Value的陣列,這是因為後期的函式呼叫Value。Call()能夠接收的引數為[]Value
var
params
=
make
([]
reflect
。
Value
,
vt
。
NumIn
())
for
i
:=
0
;
i
<
vt
。
NumIn
();
i
++
{
// In函式原型: In func(i int) Type,返回型別變數的第i個形參,返回的是一個Type型別,這裡轉換為ValueOf
params
[
i
]
=
reflect
。
ValueOf
(
vt
。
In
(
i
))
}
// 我們這裡先分別列印一下形引數組params[]和返回值陣列results[]
fmt
。
Println
(
“形引數組為: ”
,
params
)
fmt
。
Println
(
“返回值陣列為: ”
,
results
)
// 建立dimap {key:返回值型別,value:t的reflect。Value形式}
// 由於我們知道results中只有一個值,我們這裡為了方便起見直接制定了要插入的result
dimap
[
results
[
0
]]
=
t
// 列印下dimap
fmt
。
Println
(
“依賴注入表: ”
,
dimap
)
return
nil
}
可見我們當前的形引數組為空,這個很正常,因為我們
orderAccountant
本身就是沒有形參。返回值陣列只有一個main。Accountant,而dimap為
map[main。Accountant:
返回值與該函式形成了對應關係。
// 剩餘部分
func
diInvoke
(
function
interface
{})
error
{
// 。。。snip。。。
// 接下來終於輪到我們對傳入的形參進行處理了,別忘了,本函式的形參function就是order函式
// 同樣我們需要用reflect。ValueOf獲取function本身的reflect。Value結構體
var
vf
=
reflect
。
ValueOf
(
function
)
// 先判斷其是不是函式型別,不是的化報錯
if
vf
。
Kind
()
!=
reflect
。
Func
{
return
fmt
。
Errorf
(
“constructor must be a func”
)
}
// 獲取order函式的reflect。Type介面形式
var
vft
=
vf
。
Type
()
// 獲取order函式本身的形參列表,並將其放到陣列vfparams中,注意陣列型別同樣是reflect。Value
var
vfparams
=
make
([]
reflect
。
Value
,
vft
。
NumIn
())
for
i
:=
0
;
i
<
vft
。
NumIn
();
i
++
{
// 這裡是最關鍵的,dimap中的key是返回值,也就是當前order的形參(a Accountant)
// 所以我們可以用dimap[vft。In(i)]獲取對應的函式
// 然後呼叫Call(params)直接執行引數生成結果,Call將返回結果包裝為一個Value[]陣列(可能存在多個返回值)
// 這樣一來,我們就已經將function的形參都建立好了
vfparams
[
i
]
=
dimap
[
vft
。
In
(
i
)]。
Call
(
params
)[
i
]
}
// 我們這裡先分別列印一下形引數組vfparams[]
fmt
。
Println
(
“形引數組為: ”
,
vfparams
)
// 直接執行order函式
vf
。
Call
(
vfparams
)
return
nil
}
我們列印vfparams的結果為:
// 函式呼叫
func
main
()
{
diInvoke
(
order
)
}
// 顯示結果
// 需要去做假賬的老財是: 十年老會計
總體過程如下:
1 我們有一個函式:
func ctorAccountant() Accountant
2 然後將其註冊到一張表dimap裡面:
鍵key
值value
Accountant
ctorAccountant函式
3 指令函數出現:
func order(a Accountant)
4 指令函式需要傳入的形參型別是
Accountant
,於是查表找到鍵為
Accountant
的值
5 用Call方法呼叫
Accountant
鍵對應的函式
ctorAccountant
6 將返回的結果ctorAccountant返回的結果儲存在一個
vfparams
陣列中,依賴完成
7 呼叫order函式,將
vfparms
的形參傳入其中,order函式執行完畢
當然這是個最簡單模型,甚至只有兩層呼叫,接下來我們要看一個更復雜的例子:
假設我們現在存在三層組織結構:
Director(總監) -> Manager(經理) -> Accountant(老財)
Director和Manager下都有成員型別workgrounp為他們的下級,老財作為最底層只有string型別的name欄位。他們分別有各自的構建函式,他們的構建關係分別同他們的層級依賴在一起:
Director(總監)的構建需要依賴Manager(經理)的構建,Manager(經理)的構建需要依賴Accountant(老財)的構建,Accountant(老財)構建自己本身無依賴
type
Director
struct
{
workgrounp
*
Manager
}
type
Manager
struct
{
workgrounp
*
Accountant
}
type
Accountant
struct
{
name
string
}
func
ctorDirector
(
manager
*
Manager
)
*
Director
{
return
&
Director
{
workgrounp
:
manager
}
}
func
ctorManager
(
accountant
*
Accountant
)
*
Manager
{
return
&
Manager
{
workgrounp
:
accountant
}
}
func
ctorAccountant
()
*
Accountant
{
return
&
Accountant
{
name
:
“十年老會計”
}
}
同時我們要再定義一個order結構體,用於存放所有要去執行的指令:
// 宣告一個函式指標型別
type
orderFunc
func
()
// 一個用於存放財務報表的結構體
type
FinanceStatement
struct
{
assets
float32
liabilities
float32
equities
float32
}
// 三個指令函式
// 做假帳直接,反饋財務報表的資料
func
modifyFinanceStatement
()
{
var
f
=
FinanceStatement
{
assets
:
233。00
,
liabilities
:
211。00
,
equities
:
22。00
}
fmt
。
Println
(
“賬務調整完畢, 返回報告”
)
fmt
。
Printf
(
“調整好得報表為: %+v\n”
,
f
)
}
// 老財背鍋
func
blameAccountant
()
{
fmt
。
Println
(
“都是老財的錯!”
)
}
// 解僱老財
func
fireAccountant
()
{
fmt
。
Println
(
“老財被開除”
)
}
// order結構體用於存放一張map,我們將指令說明和指令函式存放其中
type
order
struct
{
orderMap
map
[
string
]
orderFunc
}
// oder的構建函式,我們將三個指令函式在order初始化的時候就註冊進去
func
ctorOrder
()
*
order
{
var
ordermap
=
make
(
map
[
string
]
orderFunc
)
ordermap
[
“做假帳”
]
=
modifyFinanceStatement
ordermap
[
“老財背鍋”
]
=
blameAccountant
ordermap
[
“解僱老財”
]
=
fireAccountant
return
&
order
{
orderMap
:
ordermap
}
}
// 未來有新的指令函式我們可以呼叫order。addOrder將其新增到order結構體的orderMap當中
func
(
o
*
order
)
addOrder
(
desc
string
,
function
orderFunc
)
{
o
。
orderMap
[
desc
]
=
function
}
Director(總監)、 Manager(經理)、 Accountant(老財)分別有成員函式execute_report_order,用於執行和反饋上級下達的任務,到了Accountant(老財)這一層,會讀取oderMap中所有需要執行的指令,並執行
func
(
director
*
Director
)
execute_report_order
(
om
map
[
string
]
orderFunc
)
{
director
。
workgrounp
。
execute_report_order
(
om
)
}
func
(
manager
*
Manager
)
execute_report_order
(
om
map
[
string
]
orderFunc
)
{
manager
。
workgrounp
。
execute_report_order
(
om
)
}
func
(
accountant
*
Accountant
)
execute_report_order
(
om
map
[
string
]
orderFunc
)
{
fmt
。
Println
(
“任務收到,開始執行。。。。執行人”
,
accountant
。
name
)
for
key
,
orderfunc
:=
range
om
{
fmt
。
Printf
(
“指令:%v ”
,
key
)
orderfunc
()
}
fmt
。
Println
(
“所有指令執行完畢”
)
}
最後我們設計一個命令函式
order()
,作用如下:
老闆對總監(Director)發出了一堆任務,總監執行
execute_report_order
函式,並反饋任務成果。
func
dispatch_order
(
d
*
Director
,
o
*
order
)
{
d
。
execute_report_order
(
o
。
orderMap
)
}
如果不使用依賴注入,我們通常要這麼呼叫任務鏈:
我們必須從下到上依次構建,這個順序不能錯
老財->經理->總監
,然後初始化
order
指令結構體
func
RunDIfactorymeTest
()
{
var
a
=
ctorAccountant
()
var
m
=
ctorManager
(
a
)
var
d
=
ctorDirector
(
m
)
var
o
=
ctorOrder
()
dispatch_order
(
d
,
o
)
}
而我們現在期望:建立一個container容器,將4個ctor構建函式都註冊到container容器中形成一張dimap,然後把order函式直接扔進container。Invoke,依賴構建自動完成,並執行order函式獲得結果。
容器的設計如下:
容器程式碼如下,參考來源:
程式碼參考
package
di
import
(
“fmt”
“reflect”
)
// DIContainer DI容器,主要是一張dimap和一個快取map
type
DIContainer
struct
{
dimap
map
[
reflect
。
Type
]
funcHandle
resultBuffer
map
[
reflect
。
Type
]
reflect
。
Value
}
// funcHandle 是函式控制代碼,主要是儲存函式指標和其形參列表
type
funcHandle
struct
{
funcPtr
reflect
。
Value
params
[]
reflect
。
Type
}
func
(
fn
*
funcHandle
)
runFunc
(
params
[]
reflect
。
Value
)
[]
reflect
。
Value
{
return
fn
。
funcPtr
。
Call
(
params
)
}
// DIContainerInitial 建立容器
func
DIContainerInitial
()
*
DIContainer
{
return
&
DIContainer
{
dimap
:
map
[
reflect
。
Type
]
funcHandle
{},
resultBuffer
:
map
[
reflect
。
Type
]
reflect
。
Value
{},
}
}
// Register 註冊函式,我們需要將外部的構建函式全部註冊到容器當中
// 1 先獲得reflect。Value形式的ctorfunction(reflect。ValueOf函式)
// 2 判斷其是否為函式型別
// 3 然後獲得其Type型別
// 4 根據其Type型別的介面函式獲得它的形參和返回值
// 5 將形參和函式本身的reflect。Value形式存放到funcHandle結構體中
// 6 將返回值型別作為key,將funcHandle作為value,存到dimap表中
func
(
c
*
DIContainer
)
Register
(
ctorfunction
interface
{})
error
{
var
funcValue
=
reflect
。
ValueOf
(
ctorfunction
)
if
funcValue
。
Kind
()
!=
reflect
。
Func
{
return
fmt
。
Errorf
(
“註冊進來的形參必須為函式型別”
)
}
var
funcType
=
funcValue
。
Type
()
// 獲取引數NumIn是返回函式的引數列表的數量
// 我們In(i)函式返回所有的引數列表,將其將其儲存在一個reflect。Type型別的陣列當中
// 實際上In函式呼叫了type。go中的in()函式,函式原型如下,其實返回的是一個所有函式引數的陣列
// 只不過in不提供對外介面,只有透過In(i)形式來獲取in[i]
// func (t *funcType) in() []*rtype {
// uadd := unsafe。Sizeof(*t)
// if t。tflag&tflagUncommon != 0 {
// uadd += unsafe。Sizeof(uncommonType{})
// }
// if t。inCount == 0 {
// return nil
// }
// return (*[1 << 20]*rtype)(add(unsafe。Pointer(t), uadd, “t。inCount > 0”))[:t。inCount:t。inCount]
// }
// 獲取所有形參列表
var
params
=
make
([]
reflect
。
Type
,
funcType
。
NumIn
())
for
i
:=
0
;
i
<
funcType
。
NumIn
();
i
++
{
params
[
i
]
=
funcType
。
In
(
i
)
}
// 獲取所有返回值列表
var
returns
=
make
([]
reflect
。
Type
,
funcType
。
NumOut
())
for
i
:=
0
;
i
<
funcType
。
NumOut
();
i
++
{
returns
[
i
]
=
funcType
。
Out
(
i
)
}
var
funchandle
=
funcHandle
{
funcPtr
:
funcValue
,
params
:
params
,
}
// 將返回值作為key,funchandle作為value註冊到dimap
for
_
,
r
:=
range
returns
{
// 判斷當前返回值對應的funchandle是否已經
if
_
,
ok
:=
c
。
dimap
[
r
];
ok
{
continue
}
c
。
dimap
[
r
]
=
funchandle
}
return
nil
}
// 列印dimap
func
(
c
*
DIContainer
)
Printdimap
()
{
fmt
。
Println
(
“the container。providers is: ”
,
c
。
dimap
)
}
// 列印resultbuffer
func
(
c
*
DIContainer
)
PrintResultsBuffer
()
{
fmt
。
Println
(
“the container。results is: ”
,
c
。
resultBuffer
)
}
// Invoke 啟動函式,我們將要執行的指令函式放入形參fn中
// 1 先獲得reflect。Value形式的fn(reflect。ValueOf函式)
// 2 判斷其是否為函式型別
// 3 然後獲得其Type型別
// 4 根據其Type型別的介面函式獲得它的形參
// 6 將fn本身的形參存入一個形引數組params
// 7 這些形參可能存在需要我們取自動化依賴構建的函式
// 8 呼叫依賴構建函式,自動對每個形參進行構建,完成後傳入形引數組
// 注意形引數組的型別是reflect。Value,這是為了對應Value。Call()函式:
// Value。Call()函式原型,可見其主要功能是先驗證v是否為函式型別,再呼叫call指令執行函式
// func (v Value) Call(in []Value) []Value {
// v。mustBe(Func)
// v。mustBeExported()
// return v。call(“Call”, in)
// }
func
(
c
*
DIContainer
)
Invoke
(
fn
interface
{})
error
{
var
funcValue
=
reflect
。
ValueOf
(
fn
)
if
funcValue
。
Kind
()
!=
reflect
。
Func
{
return
fmt
。
Errorf
(
“註冊進來的形參必須為函式型別”
)
}
var
funcType
=
funcValue
。
Type
()
// 獲取fn本身的形參存入臨時的形引數組
var
err
error
var
params
=
make
([]
reflect
。
Value
,
funcType
。
NumIn
())
for
i
:=
0
;
i
<
funcType
。
NumIn
();
i
++
{
params
[
i
],
err
=
c
。
diConstructor
(
funcType
。
In
(
i
))
if
err
!=
nil
{
return
err
}
}
funcValue
。
Call
(
params
)
return
nil
}
// diConstructor 依賴構建器
// 1 傳入param的是外部指令函式的形參,先檢視resultBuffer中是否已經有對應的結果,如果有直接返回
// 2 去dimap檢視是否存在該形參對應的param
// 3 不存在就返回一個ValueOf(param)將其轉換為Value模式
// 4 如果存在則獲取相應的函式控制代碼funchandle = dimap[parm],不存在返回error
// 5 然後解包funchandle,讀取對應的的funchandle。params
// 6 對每個funchandle。params進行遞迴地呼叫diConstructor(),直道遞迴到不存在形參無依賴的底層函式
// 7 執行函式,層層返回結果
// 8 將函式執行的結果儲存在一個results陣列中陣列型別為reflect。Value
// 9 將其寫入容器的resultBuffer中
func
(
c
*
DIContainer
)
diConstructor
(
param
reflect
。
Type
)
(
val
reflect
。
Value
,
err
error
)
{
// 如果容器的resultBuffer已經存在形參param和result的對應,那麼直接返回
if
result
,
ok
:=
c
。
resultBuffer
[
param
];
ok
{
return
result
,
nil
}
// dimap中找不到對應的函式控制代碼
var
funchandle
,
ok
=
c
。
dimap
[
param
]
if
!
ok
{
return
reflect
。
Value
{},
fmt
。
Errorf
(
“當前形參%s, 沒有對應的函式控制代碼”
,
param
)
}
// 建立一個reflect。Value的陣列,用來存放所有funchandle。params
var
params
=
make
([]
reflect
。
Value
,
len
(
funchandle
。
params
))
for
i
,
p
:=
range
funchandle
。
params
{
// 這裡需要對每個funchandle裡的形參進行遞迴構建
// 逐步講解如下:
// 1 是order(d *Director),然後我們獲取order的形參Director並執行diConstructor(Director)
// 2 對d進行查表後得到其對應的map[Director] = ctorDirector
// 3 對ctorDirector的函式控制代碼進行解包,重新獲得其函式形參ctorDirector(manager *Manager)
// 4 將ctorDirector的形參重新作為形參傳遞迴遞給diConstructor(Manager)
// 5 查表後獲得map[Manager] = ctorManager
// 6 對ctorManage的函式控制代碼進行解包,重新獲得其函式形參ctorManager(accountant *Accountant)
// 7 將ctorManager的形參重新作為形參傳遞給diConstructor(Accountant)
// 8 查表後獲得map[Accountant] = ctorAccountant
// 9 對ctorAccountant的函式控制代碼進行解包
// 10 重新獲得其函式形參ctorAccountant()為reflect。Type型別空值
// 11 繼續呼叫diConstructor([])
// 12 空的形參會跳過for i, p := range funchandle。params{}這個迴圈體,直接執行依賴函式
params
[
i
],
err
=
c
。
diConstructor
(
p
)
}
// 執行依賴函式,如果已經來到了依賴底層,那麼這裡的params是一個reflect。Value型別的空陣列
// results 返回的是一個Value型別的結果陣列[]reflect。Value
// 假設我們這裡如果呼叫的是最深層的Accountant結構體
// results = [Accountant]
var
results
=
funchandle
。
runFunc
(
params
)
// 遍歷返回結果並做驗證
for
_
,
result
:=
range
results
{
// 如果results中返回的結果有error型別(diConstructor其中一個返回值是error),則報錯
if
validateError
(
result
。
Type
())
&&
!
result
。
IsNil
()
{
return
reflect
。
Value
{},
fmt
。
Errorf
(
“%+v 函式控制代碼呼叫錯誤: %+v”
,
funchandle
,
result
)
}
// 不存在error且不為nil,則將其儲存到容器的resultBuffer中
if
!
validateError
(
result
。
Type
())
&&
!
result
。
IsNil
()
{
// 這裡我們放個除錯列印資訊,可以方便我們逐步檢視resultbuffer的變化
fmt
。
Println
(
“c。resultBuffer[”
,
result
。
Type
(),
“] = ”
,
result
)
c
。
resultBuffer
[
result
。
Type
()]
=
result
}
}
// 這裡我們放個除錯列印資訊,可以方便我們逐步檢視每次返回的內容
fmt
。
Println
(
“c。resultBuffer[”
,
param
,
“] = ”
,
c
。
resultBuffer
[
param
])
return
c
。
resultBuffer
[
param
],
nil
}
func
validateError
(
t
reflect
。
Type
)
bool
{
if
t
。
Kind
()
!=
reflect
。
Interface
{
return
false
}
// Implements reports whether the type implements the interface type u。
// 原型:Implements(u Type) bool
// reflect。TypeOf(reflect。TypeOf((*error)(nil)) => *error
// reflect。TypeOf(reflect。TypeOf((*error)(nil))。Elem() => error
// 這裡也驗證了Elem()是解引用的操作
// 實際上這條可以寫為: t。Implements(error)
return
t
。
Implements
(
reflect
。
TypeOf
(
reflect
。
TypeOf
((
*
error
)(
nil
))。
Elem
()))
}
呼叫部分程式碼如下:
package
di
import
(
“fmt”
)
type
Director
struct
{
workgrounp
*
Manager
}
type
Manager
struct
{
workgrounp
*
Accountant
}
type
Accountant
struct
{
name
string
}
type
FinanceStatement
struct
{
assets
float32
liabilities
float32
equities
float32
}
type
orderFunc
func
()
func
modifyFinanceStatement
()
{
var
f
=
FinanceStatement
{
assets
:
233。00
,
liabilities
:
211。00
,
equities
:
22。00
}
fmt
。
Println
(
“賬務調整完畢, 返回報告”
)
fmt
。
Printf
(
“調整好得報表為: %+v\n”
,
f
)
}
func
blameAccountant
()
{
fmt
。
Println
(
“都是老財的錯!”
)
}
func
fireAccountant
()
{
fmt
。
Println
(
“老財被開除”
)
}
type
order
struct
{
orderMap
map
[
string
]
orderFunc
}
func
ctorOrder
()
*
order
{
var
ordermap
=
make
(
map
[
string
]
orderFunc
)
ordermap
[
“做假帳”
]
=
modifyFinanceStatement
ordermap
[
“老財背鍋”
]
=
blameAccountant
ordermap
[
“解僱老財”
]
=
fireAccountant
return
&
order
{
orderMap
:
ordermap
}
}
func
(
o
*
order
)
addOrder
(
desc
string
,
function
orderFunc
)
{
o
。
orderMap
[
desc
]
=
function
}
func
ctorDirector
(
manager
*
Manager
)
*
Director
{
return
&
Director
{
workgrounp
:
manager
}
}
func
ctorManager
(
accountant
*
Accountant
)
*
Manager
{
return
&
Manager
{
workgrounp
:
accountant
}
}
func
ctorAccountant
()
*
Accountant
{
return
&
Accountant
{
name
:
“十年老會計”
}
}
func
(
director
*
Director
)
execute_report_order
(
om
map
[
string
]
orderFunc
)
{
director
。
workgrounp
。
execute_report_order
(
om
)
}
func
(
manager
*
Manager
)
execute_report_order
(
om
map
[
string
]
orderFunc
)
{
manager
。
workgrounp
。
execute_report_order
(
om
)
}
func
(
accountant
*
Accountant
)
execute_report_order
(
om
map
[
string
]
orderFunc
)
{
fmt
。
Println
(
“任務收到,開始執行。。。。執行人”
,
accountant
。
name
)
for
key
,
orderfunc
:=
range
om
{
fmt
。
Printf
(
“指令:%v ”
,
key
)
orderfunc
()
}
fmt
。
Println
(
“所有指令執行完畢”
)
}
func
dispatch_order
(
d
*
Director
,
o
*
order
)
{
d
。
execute_report_order
(
o
。
orderMap
)
}
func
RunDIfactorymeTest
()
{
// 初始化容器
var
dicontainer
=
DIContainerInitial
()
// 將構建函式全部註冊進去,可以不必理會順序
if
err
:=
dicontainer
。
Register
(
ctorAccountant
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
dicontainer
。
Register
(
ctorManager
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
dicontainer
。
Register
(
ctorDirector
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
dicontainer
。
Register
(
ctorOrder
);
err
!=
nil
{
panic
(
err
)
}
// 列印一下函式註冊後的dimap
fmt
。
Println
(
“dicontainer。dimap: ”
)
dicontainer
。
Printdimap
()
// 列印一下函式Invoke執行前的resultbuffer,應該為空
fmt
。
Println
(
“dicontainer。resultbuffer before Invoke(): ”
)
dicontainer
。
PrintResultsBuffer
()
// 啟動Invoke指令,我們將命令下達
var
err
=
dicontainer
。
Invoke
(
dispatch_order
)
// 列印一下函式Invoke執行後的resultbuffer,應該所有依賴構建後的資料都儲存其中
fmt
。
Println
()
fmt
。
Println
(
“container。resultbuffer after Invoke(): ”
)
dicontainer
。
PrintResultsBuffer
()
if
err
!=
nil
{
panic
(
err
)
}
}
// 列印顯示:
// 任務收到,開始執行。。。。執行人 十年老會計
// 指令:解僱老財 老財被開除
// 指令:做假帳 賬務調整完畢, 返回報告
// 調整好得報表為: {assets:233 liabilities:211 equities:22}
// 指令:老財背鍋 都是老財的錯!
// 所有指令執行完畢
執行結果: