原创文章转载请注明出处
前文《go利用(*interface{})(nil)传递参数类型》介绍了Inject包是如何利用(*interface{})(nil)通过Map和MapTo将数据存储到values map[reflect.Type]reflect.Value
这个map中。
今天来聊聊Invoke方法
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type.
// Returns a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
// It panics if f is not a 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
}
其实没有太多有技术含量的东西,只要把反射吃透了,再弄清楚前文中Map
和MapTo
存储的类型数据映射map,那么go的依赖注入就这么赤裸裸的展现在你眼前。
将函数的值从空接口中反射出来,然后使用reflect.Call
来传递参数并调用它。参数个数从t.NumIn()
获取,循环遍历参数类型,再通过Get
方法从values map[reflect.Type]reflect.Value
获取对应类型的数据。
func (i *injector) Get(t reflect.Type) reflect.Value {
val := i.values[t]
if val.IsValid() {
return val
}
// no concrete types found, try to find implementors
// if t is an interface
if t.Kind() == reflect.Interface {
for k, v := range i.values {
if k.Implements(t) {
val = v
break
}
}
}
// Still no type found, try to look it up on the parent
if !val.IsValid() && i.parent != nil {
val = i.parent.Get(t)
}
return val
}
这里还有一篇文章《在 GOLANG 中用名字调用函数》,不是针对Martini
的源码进行分析,但是读完以后你就会更明白为何Invoke
方法要这么麻烦去用反射和依赖注入来书写。
我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。
网友评论