Invoke如何动态传参

原创文章转载请注明出处

前文《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
}

其实没有太多有技术含量的东西,只要把反射吃透了,再弄清楚前文中MapMapTo存储的类型数据映射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方法要这么麻烦去用反射和依赖注入来书写。

我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。

你可能感兴趣的:(Invoke如何动态传参)