原创文章转载请注明出处
最近阅读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如何动态传参》
我是咕咕鸡,一个还在不停学习的全栈工程师。热爱生活,喜欢跑步,家庭是我不断向前进步的动力。
本文转载自: 掘金