原创文章转载请注明出处
最近阅读Martini的源码,读到了inject这部分,inject在Martini的代码中无处不在,是Martini框架的绝对核心。
先看看injector类型的声明:
1 | elm复制代码type injector struct { |
撇开 parent不看,values是一个映射表,用于保存注入的参数,它是一个用reflect.Type当键、reflect.Value为值的map。
parent Injector又是什么鬼?
1 | reasonml复制代码// Injector represents an interface for mapping and injecting dependencies into structs |
Injector是注入接口声明的组合,我们先关注TypeMapper这个接口,从源码可以得知Map和MapTo是用来映射数据类型和数据到values map[reflect.Type]reflect.Value的方法。
Map方法相对来说比较简单,利用反射获取对象的type。
1 | stylus复制代码func (i *injector) Map(val interface{}) TypeMapper { |
现在我们先假设参数中有多个string时,values map[reflect.Type]reflect.Value这个map只会保存最后一个string的映射,那我们该如何处理才能完整的保存所有的string参数呢?
考虑interface类型在底层的实现(type,data),inject库实现了一个从interface指针中获取类型的函数InterfaceOf,而MapTo则利用InterfaceOf来获取传入的数据类型。
1 | stylus复制代码func InterfaceOf(value interface{}) reflect.Type { |
简直是神来之笔,再找个别人的例子:
1 | go复制代码package main |
输出
1 | haxe复制代码interface {} |
看到了吗?指向接口的空指针,虽然data是nil,但是我们只要它的type。分步解释一下:
1 | stylus复制代码//以(*SpecialString)(nil)为例 |
interface{}是什么,在go里面interface{}就是万能的Any。inject利用了(*interface{})(nil)携带数据类型的特点,只用一个空指针就搞定了数据类型的传输,而且扩展了同类型数据的绑定。
让我们到martini.go去看看这个注入是怎么用的吧。
1 | stylus复制代码// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level. |
自定义的Martini结构体包含了inject.Injector接口,所以可以很方便的注入logger。后续Invoke中间件的时候,自然就可以通过Injector的Get方法获取logger对象。context则使用了MapTo方法注入了Context和http.ResponseWriter这两个接口类型。
那么Invoke的时候又是如何调用函数并且注入参数的呢?请移步《Invoke如何动态传参》
我是咕咕鸡,一个还在不停学习的全栈工程师。热爱生活,喜欢跑步,家庭是我不断向前进步的动力。
本文转载自: 掘金