nject 是依賴注入的Go語言實現,它能在運行時注入參數,調用方法,是 Martini 框架(Go語言中著名的 Web 框架)的基礎核心。
demo1:
package main import ( "fmt" "github.com/codegangsta/inject" ) type S1 interface{} type S2 interface{} func Format(name string, company S1, level S2, age int) { fmt.Printf("name = %s, company=%s, level=%s, age = %d!\n", name, company, level, age) } func main() { //控制實例的創建 inj := inject.New() //實參注入 inj.Map("tom") inj.MapTo("tencent", (*S1)(nil)) inj.MapTo("T4", (*S2)(nil)) inj.Map(23) //函數反轉調用 inj.Invoke(Format) }
可見 inject 提供了一種注入參數調用函數的通用功能,inject.New() 相當於創建了一個控制實例,由其來實現對函數的注入調用。inject 包不但提供了對函數的注入,還實現了對 struct 類型的注入,Apply 方法是用於對 struct 的字段進行注入,參數為指向底層類型為結構體的指針。可注入的前提是:字段必須是導出的(也即字段名以大寫字母開頭),並且此字段的 tag 設置為`inject`
。示例代碼如下所示
package main import ( "fmt" "github.com/codegangsta/inject" ) type S1 interface{} type S2 interface{} type Staff struct { Name string `inject` Company S1 `inject` Level S2 `inject` Age int `inject` } func main() { //創建被注入實例 s := Staff{} //控制實例的創建 inj := inject.New() //初始化注入值 inj.Map("tom") inj.MapTo("tencent", (*S1)(nil)) inj.MapTo("T4", (*S2)(nil)) inj.Map(23) //實現對 struct 注入 inj.Apply(&s) //打印結果 fmt.Printf("s = %v\n", s) }
另外 Injector 還規定了 SetParent 行為,它用於設置父 Injector,其實它相當於查找繼承。也即通過 Get 方法在獲取被注入參數時會一直追溯到 parent,這是個遞歸過程,直到查找到參數或為 nil 終止
Map 和 MapTo 方法都用於注入參數,保存於 injector 的成員 values 中。這兩個方法的功能完全相同,唯一的區別就是 Map 方法用參數值本身的類型當鍵,而 MapTo 方法有一個額外的參數可以指定特定的類型當鍵。但是 MapTo 方法的第二個參數 ifacePtr 必須是接口指針類型,因為最終 ifacePtr 會作為 InterfaceOf 方法的參數。
為什么需要有 MapTo 方法?因為注入的參數是存儲在一個以類型為鍵的 map 中,可想而知,當一個函數中有一個以上的參數的類型是一樣時,后執行 Map 進行注入的參數將會覆蓋前一個通過 Map 注入的參數。
SetParent 方法用於給某個 Injector 指定父 Injector。Get 方法通過 reflect.Type 從 injector 的 values 成員中取出對應的值,它可能會檢查是否設置了 parent,直到找到或返回無效的值,最后 Get 方法的返回值會經過 IsValid 方法的校驗。
demo3:
package main import ( "fmt" "github.com/codegangsta/inject" ) type SpecialString interface{} func Say(name string, gender SpecialString, age int) { fmt.Printf("My name is %s, gender is %s, age is %d!\n", name, gender, age) } /* func Say(name string, gender string, age int) { fmt.Printf("My name is %s, gender is %s, age is %d!\n", name, gender, age) } */ //沒有定義 SpecialString 接口作為 gender 參數的類型,而把 name 和 gender 都定義為 string 類型,那么 gender 會覆蓋 name 的值 func main() { inj := inject.New() inj.Map("張三") inj.MapTo("男", (*SpecialString)(nil)) //inj.Map("男") inj2 := inject.New() inj2.Map(25) inj.SetParent(inj2) inj.Invoke(Say) }