依賴注入是軟件工程中常常使用到的一種技術,它提供了一種控制反轉的機制,把控制權利交給了調用方。調用方來決定使用哪些參數,哪些對象來進行具體的業務邏輯。java
它有幾個好處:
1 它讓調用方更靈活。
2 大量減小定義類型的代碼量
3 增長代碼的可用性,由於調用方只須要關注它須要的參數,不須要顧及它不須要的參數了。git
依賴注入使用最多的應該是java中的spring框架了。依賴注入在使用的時候但願調用函數的參數是不固定的。github
function Action(a TypeA, b TypeB)
就是說,這個Action在實際調用的時候,能夠任意加參數,每次加一個參數類型,都有一個容器能夠給這個Action調用函數傳遞對應的參數對象提供使用。golang
Golang中也有項目是使用依賴注入實現的,martini就是一個依靠依賴注入實現的web框架,它的做者開源的https://github.com/codegangsta/inject 項目也就很值得咱們學習。web
這個inject項目很小,實際代碼就一個文件,很容易閱讀。spring
// Injector表明依賴注入的容器須要實現的接口 type Injector interface { Applicator // 這個接口用來灌入到一個結構體 Invoker // 這個接口用來實際調用的,因此能夠實現非反射的實際調用 TypeMapper // 這個接口是真正的容器 // SetParent sets the parent of the injector. If the injector cannot find a // dependency in its Type map it will check its parent before returning an // error. SetParent(Injector) // 表示這個結構是遞歸的 }
這個Injector使用三個接口進行組合,每一個接口有各自不一樣的用處。安全
TypeMapper是依賴注入最核心的容器部分,注入類型和獲取類型都是這個接口承載的。
Invoker和Applicator都是注入部分,Invoker將TypeMapper容器中的數據注入到調用函數中。而Applicator將容器中的數據注入到實體對象中。
最後咱們還將Injector容器設計爲有層級的,在咱們獲取容器數據的時候,會先從當前容器找,找不到再去父級別容器中找。併發
這幾個接口中的TypeMapper又值得看一下:app
// TypeMapper represents an interface for mapping interface{} values based on type. // TypeMapper是用來做爲依賴注入容器的,設置的三種方法都是鏈式的 type TypeMapper interface { // Maps the interface{} value based on its immediate type from reflect.TypeOf. // 直接設置一個對象,TypeOf是key,value是這個對象 Map(interface{}) TypeMapper // Maps the interface{} value based on the pointer of an Interface provided. // This is really only useful for mapping a value as an interface, as interfaces // cannot at this time be referenced directly without a pointer. // 將一個對象注入到一個接口中,TypeOf是接口,value是對象 MapTo(interface{}, interface{}) TypeMapper // Provides a possibility to directly insert a mapping based on type and value. // This makes it possible to directly map type arguments not possible to instantiate // with reflect like unidirectional channels. // 直接手動設置key和value Set(reflect.Type, reflect.Value) TypeMapper // Returns the Value that is mapped to the current type. Returns a zeroed Value if // the Type has not been mapped. // 從容器中獲取某個類型的注入對象 Get(reflect.Type) reflect.Value }
這裏的Map是將數據注入,即將數據類型和數據值進行映射存儲在容器中。MapTo是將數據接口和數據值進行映射存儲在容器中。Set就是手動將數據類型活着數據接口和數據值存儲在容器中。Get則和Set相反。框架
咱們能夠看下inject文件中實現了這個接口的對象:injector
// 實際的注入容器,它實現了Injector的全部接口 type injector struct { // 這個就是容器最核心的map values map[reflect.Type]reflect.Value // 這裏設置了一個parent,因此這個Inject是能夠嵌套的 parent Injector }
其中的這個map[reflect.Type]reflect.Value就是最核心的。那麼這裏就須要注意到了,這個inject其實是一個基礎的map,而不是線程安全的map。因此若是在併發場景下,不該該在併發請求中進行動態注入或者改變容器元素。不然頗有可能出現各類線程安全問題。
咱們能夠看看Map,Set等函數作的事情就是設置這個Map
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
下一個重要的函數就Invoke。
這個Invoke作的事情咱們也能很容易想清,根據它自己裏面的函數參數類型,一個個去容器中拿對應值。
// 真實的調用某個函數f,這裏的f默認是function func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { t := reflect.TypeOf(f) var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func for i := 0; i < t.NumIn(); i++ { argType := t.In(i) val := inj.Get(argType) if !val.IsValid() { return nil, fmt.Errorf("Value not found for type %v", argType) } in[i] = val } return reflect.ValueOf(f).Call(in), nil }
注:inject相關的中文註釋代碼解讀在項目:https://github.com/jianfengye/inside-go 中。
無聞在matini基礎上又封裝了一層inject。它使用的方法是直接保留CopyRight的通知,將https://github.com/codegangsta/inject 這個類作了一些修改。
我看了下這些修改,主要是增長了一個FastInvoker
// FastInvoker represents an interface in order to avoid the calling function via reflection. // // example: // type handlerFuncHandler func(http.ResponseWriter, *http.Request) error // func (f handlerFuncHandler)Invoke([]interface{}) ([]reflect.Value, error){ // ret := f(p[0].(http.ResponseWriter), p[1].(*http.Request)) // return []reflect.Value{reflect.ValueOf(ret)}, nil // } // // type funcHandler func(int, string) // func (f funcHandler)Invoke([]interface{}) ([]reflect.Value, error){ // f(p[0].(int), p[1].(string)) // return nil, nil // } type FastInvoker interface { // Invoke attempts to call the ordinary functions. If f is a function // with the appropriate signature, f.Invoke([]interface{}) is a Call that calls f. // Returns a slice of reflect.Value representing the returned values of the function. // Returns an error if the injection fails. Invoke([]interface{}) ([]reflect.Value, error) }
而且在Invoke調用的地方增長了一個分支,若是這個調用函數是自帶有Invoke方法的,那麼就用一種不用反射的方式。
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { t := reflect.TypeOf(f) switch v := f.(type) { case FastInvoker: return inj.fastInvoke(v, t, t.NumIn()) default: return inj.callInvoke(f, t, t.NumIn()) } }
我以爲這個fastInvoke是神來之筆啊。咱們使用Golang的inject最懼怕的就是性能問題。這裏的Invoke頻繁使用了反射,因此會致使Invoke的性能不會很高。可是咱們有了fastInvoke替換方案,當須要追求性能的時候,咱們就可使用fastInvoke的方法進行替換。
因此我下面的這個示例是最好的理解inject的例子:
package main import "gopkg.in/macaron.v1" import "github.com/go-macaron/inject" import "fmt" import "reflect" type A struct { Name string } type B struct { Name string } func (b *B) GetName() string { return b.Name } type I interface { GetName() string } type C struct { AStruct A `inject` BStruct B `inject` } type MyFastInvoker func(arg1 A, arg2 I, arg3 string) func (invoker MyFastInvoker) Invoke(args []interface{}) ([]reflect.Value, error) { if a, ok := args[0].(A); ok { fmt.Println(a.Name) } if b, ok := args[1].(I); ok { fmt.Println(b.GetName()) } if c, ok := args[2].(string); ok { fmt.Println(c) } return nil, nil } type Invoker2 struct { inject.Injector } func main() { InjectDemo() a := &A{Name: "inject name"} m := macaron.Classic() m.Map(a) m.Get("/", func(a *A) string { return "Hello world!" + a.Name }) m.Run() } func InjectDemo() { a := A{Name: "a name"} inject1 := inject.New() inject1.Map(a) inject1.MapTo(&B{Name: "b name"}, (*I)(nil)) inject1.Set(reflect.TypeOf("string"), reflect.ValueOf("c name")) inject1.Invoke(func(arg1 A, arg2 I, arg3 string) { fmt.Println(arg1.Name) fmt.Println(arg2.GetName()) fmt.Println(arg3) }) c := C{} inject1.Apply(&c) fmt.Println(c.AStruct.Name) inject2 := inject.New() inject2.Map(a) inject2.MapTo(&B{Name: "b name"}, (*I)(nil)) inject2.Set(reflect.TypeOf("string"), reflect.ValueOf("c name")) inject2.Invoke(MyFastInvoker(nil)) }
輸出:
a name b name c name a name b name c name
上面那個例子能看懂基本就掌握了inject的使用了。