1,反射中调用 函数
既然函数可以像普通的类型变量一样可以的话,那么在反射机制中就和不同的变量是一样的,在反射中 函数 和 方法 的类型(Type)都是 reflect.Func,如果要调用函数的话,可以通过 Value 的 Call() 方法
Value 的 Call() 方法的参数是一个 Value 的 slice,对应的反射函数类型的参数,返回值也是一个 Value 的 slice,同样对应反射函数类型的返回值。通过这个例子,相信你一看就明白了:
func prints(i int) string {
fmt.Println(“i =”, i)
return strconv.Itoa(i)
}
func main() {
fv := reflect.ValueOf(prints)
params := make([]reflect.Value, 1) // 参数
params[0] = reflect.ValueOf(20) // 参数设置为20
rs := fv.Call(params) // rs作为结果接受函数的返回值
fmt.Println(“result:”, rs[0].Interface().(string)) // 当然也可以直接是 rs[0].Interface()
2,反射中调用 方法
函数和方法可以说其实本质上是相同的,只不过方法与一个“对象”进行了“绑定”,方法是“对象”的一种行为,这种行为是对于这个“对象”的一系列操作,例如修改“对象”的某个属性
Method 和 MethodByName 的API,
ype MyType struct {
i int
name string
}
func (mt *MyType) SetI(i int) {
mt.i = i
}
func (mt *MyType) SetName(name string) {
mt.name = name
}
func (mt *MyType) String() string {
return fmt.Sprintf(“%p”, mt) + “–name:” + mt.name + “ i:” + strconv.Itoa(mt.i)
}
func main() {
myType := &MyType{22, “golang”}
//fmt.Println(myType) // 就是检查一下myType对象内容
//println(“—————”)
mtV := reflect.ValueOf(&myType).Elem()
// 也可以使用
//mtV := reflect.ValueOf(myType)
fmt.Println("Before:", mtV.MethodByName("String").Call(nil)[0])
params := make([]reflect.Value, 1)
params[0] = reflect.ValueOf(18)
mtV.MethodByName("SetI").Call(params)
params[0] = reflect.ValueOf("reflection test")
mtV.MethodByName("SetName").Call(params)
fmt.Println("After:", mtV.MethodByName("String").Call(nil)[0]) } mtV := reflect.ValueOf(&myType).Elem() fmt.Println("Before:",mtV.Method(2).Call(nil)[0]) params = make([]reflect.Value,1) params[0] = reflect.ValueOf(18) mtV.Method(0).Call(params) params[0] = reflect.ValueOf("reflection test") mtV.Method(1).Call(params) fmt.Println("After:",mtV.Method(2).Call(nil)[0]) <!-- more --> martini的整个框架的后台动力都是来自inject这个包,它其实是独立于框架的一个功能包,里面主要是运用了反射的技术来完成所需要的功能。我自己在学习或者接触一段新代码或者知识点的时候都会先直接找sample运行看跑的结果,所以下面先看一段代码及执行结果<代码1>:
package main
import (
"fmt"
"github.com/codegangsta/inject"
"log"
"reflect"
)
func do(i int) int {
fmt.Println(i)
return 23
}
func main() {
in := inject.New()
in.Map(12)
val, err := in.Invoke(do)
if err != nil {
log.Fatal("调用出错:" + err.Error())
return
}
for _, v := range val {
if v.Kind() == reflect.Int {
fmt.Println(v.Int())
}
}
}
输出结果为: 12
23
下面根据上面的代码来一步步分析下inject的执行过程与原理。
代码结构
有一个inject结构
type injector struct {
values map[reflect.Type]reflect.Value
parent Injector
}
values是用来存储每一个
代码1中,in := inject.New()就是创建了一个inject结构,并返回。代码就是
// New returns a new Injector.
func New() Injector {
return &injector{
values: make(map[reflect.Type]reflect.Value),
}
}
下面是in.Map(12)这条语句,顾名思义就是将12这个数值进行映射,在inject中唯一可以存储映射的就只有values了,那么它当然就应该存储到values里面。代码如下:
// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
// It returns the TypeMapper registered in.
func (i *injector) Map(val interface{}) TypeMapper {
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
return i
}
返回的这个TypeMapper是一个接口
// TypeMapper represents an interface for mapping interface{} values based on type.
type TypeMapper interface {
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
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.
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.
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
}
下面我们会看到其实inject这个结构实现了TypeMapper这个接口。所以返回值我们也可以理解为inject这个结构本身。是否需要这个返回值根据情况而定,大多数时候都不需要(但是不管你要不要,它都给你返回了^_^)。
下面回到Map这个方法,它使用了reflect这个反射包,这样就可以将需要映射的12这个值存储到了values中了,关于reflect这个包的用法可以查看官方的文档。
既然函数的参数已经映射完成了,那么久可以执行调用操作了。in.Invoke(do)就是调用do方法,所以Map这个方法其实是为了将所要调用的函数的参数事先存储到in这个实例中服务的,可能看到这里大家就会想,上面TypeMapper接口中不是还有一个MapTo吗,长的跟Map这么像是干嘛的,嗯,当然不是耍帅用的,最开始的时候,介绍inject这个结构的时候说,values是个map类型的,每一个类型只能对应一个唯一的值,那么下面问题来了:我们函数的参数类型不可能都不一样吧。do函数只有一个参数,可我们要是再加一个同样是int类型的参数呢,岂不是要把之前的参数值给覆盖掉了,为了解决这个问题引入了MapTo这个方法,完成的功能和Map是一样的,只是多了一个参数。go语言中我们可以基于一个已有类型建立一个新类型,type myInt interface{},那么myInt就是基于interface{}的自建的类型了,interface{}这个就相当于python或者 java中的Object,C/C++中的void*。既然不能两个都叫int,那么我就换一个名字改叫myInt好了,这下就不会冲突了。同样,要是还有一个int型的参数,那就再定义一个myInt2等等,总之不跟你一个样就好了。 例如:本来是do(i,j int),那么现在就变成了do(i int,j myInt)这个样子了。
回到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
} var in = make([]reflect.Value, t.NumIn())这条语句创建了一个in变量,这个变量的作用就是用来存储被调用的函数的参数值的。t.NumIn()这个方法当t的类型不是函数的时候会直接panic,正常返回的是f这个函数的参数的个数,可以参看官方的文档。
既然是调用,那么肯定是需要参数的,而参数之前又已经存储到了values中,所以现在只需要到values中把参数取出来就OK了。由于values是一个
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
} 首先根据类型t从values中取值,取值当然有取失败的,所以接下去就判断一个这个值是否是可用的,isValid(),要是可用的就直接返回这个值,否则就说明values中不存在这个类型t对应的值,那接下去Get会查看类型t是否实现了values中某接口类型,如果确实这样,那么就返回这个接口类型所对应的值。如果还找不到,那么Get还会尽最后的努力请求老爸,看看他那里是否有这个类型或者相关接口(代码也拼爹啊?)。
假设一切顺利,那么现在in这个变量已经存储了被调用函数的所有参数值了,万事俱备,只欠Call,那就Call,Call方法的参数就是存储有被调用函数的参数切片in,返回值为被调用函数f的返回值,这里需要注意的是虽然它的返回值是f的返回值,但是它是一个[]reflect.Value切片,使用的时候自己根据相关方法进行类型转换。
除了上面提到的方法之外,inject源码中还有一个很帅气的方法,Apply
package main
import (
"fmt"
"github.com/codegangsta/inject"
)
type MyApp struct {
Name string `inject`
Age int `inject`
}
func main() {
app := MyApp{}
in := inject.New()
in.Map("zhengk")
in.Map(25)
in.Apply(&app)
fmt.Println(app.Name)
fmt.Println(app.Age)
} 运行结果为: zhengk 25
还有一个就是为拼爹使用的找爹函数SetParent,功能:给当前inject实例设置一个父亲,在Get方法里面也见过它的使用场景。给一个简单的例子吧
package main
import (
"fmt"
"github.com/codegangsta/inject"
)
func do(i int, j myInt, name string) {
fmt.Println(i, j, name)
}
type myInt interface{}
func main() {
inP := inject.New()
inP.Map("zhengk")
inChild := inject.New()
inChild.Map(25)
inChild.MapTo(12, (*myInt)(nil))
inChild.SetParent(inP)
inChild.Invoke(do)
} 运行结果为:25 12 zhengk package inject
import (
“fmt”
“reflect”
)
// 实现回调函数参数值的注入,通观martini框架,Injector就是用于存储中间件函数以及用户自定义函数回调时的参数
type Injector interface {
Applicator // 结构体各个字段赋值
Invoker // 通过反射实现函数调用的接口
TypeMapper // 类型映射接口,TypeMapper 的功能与系统设计初衷有关系吧,mapper根据类型为键值key,同一类型只有一个value与之对应
SetParent(Injector)
}
// 为结构体类型的各个字段进行动态赋值
type Applicator interface {
// 为结构体定义中,带有’inject’标签的字段进行赋值
Apply(interface{}) error
}
// 通过反射的方式实现函数调用
type Invoker interface {
// 参数类型为函数类型,使用反射获取函数参数类型,根据各个参数类型获取对应的值,并调用该函数
Invoke(interface{}) ([]reflect.Value, error)
}
// 参数值与类型的映射接口
type TypeMapper interface {
// 具体类型的映射,根据值的具体类型直接建立映射
Map(interface{}) TypeMapper
// ’泛型‘与具体类型之间的映射,第二个参数必须是泛型的指针类型
// 因为这里只需要获得泛型的类型,只有泛型的指针代价最小了,因为只需要用到它的type部分
// 这里需要理解interface{}的具体实现
MapTo(interface{}, interface{}) TypeMapper
Set(reflect.Type, reflect.Value) TypeMapper
Get(reflect.Type) reflect.Value // 根据类型查找值
}
//这里类似于将所有的golang类型分为两种大的类型:泛型 interface{}与具体类型concrete type
type injector struct {
values map[reflect.Type]reflect.Value // 存储上述过程中结构体字段的值,以及函数调用参数的值
parent Injector //
}
// 查询 value 的接口类型, 适用于interface{}类型的映射, 如果value不是指针类型将会返回异常
// 这个接口只是用在泛型映射,因此只需要从value(interface{})中获取type部分
func InterfaceOf(value interface{}) reflect.Type {
t := reflect.TypeOf(value) // 获取type的值
for t.Kind() == reflect.Ptr { // 如果是指针,继续查指针的指向
t = t.Elem() // 指针指向的元素
}
if t.Kind() != reflect.Interface { // 最后肯定是指向interface 了,如果不是,说明传入的参数有问题
panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
}
return t }
// New returns a new Injector.
//创建一个注入工具,提供数据存储与查询,其他功能主要还是依赖于反射库的实现
func New() Injector {
return &injector{
values: make(map[reflect.Type]reflect.Value),
}
}
// 参数类型为函数func,使用反射接口完成函数的调用
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
t := reflect.TypeOf(f) //获取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
}
// 反射类型的函数调用,取出f中的数据部分,即实例值函数
return reflect.ValueOf(f).Call(in), nil // 函数调用 }
// 为结构体赋值的具体实现
func (inj *injector) Apply(val interface{}) error {
v := reflect.ValueOf(val)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil // Should not panic here ?
}
t := v.Type() // 获取结构体的类型
for i := 0; i < v.NumField(); i++ { // 获取字段的数量
f := v.Field(i)
structField := t.Field(i)
// 带有'inject'标签的才可能被赋值
if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") {
ft := f.Type()
v := inj.Get(ft)
if !v.IsValid() {
return fmt.Errorf("Value not found for type %v", ft)
}
f.Set(v)
}
}
return nil }
// 具体类型参数,类型与值的映射
func (i *injector) Map(val interface{}) TypeMapper {
//类型与值的映射
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
return i
}
// 泛型类型的映射
func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
//需要先获取泛型指针指向的具体泛型类型
i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
return i
}
func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
i.values[typ] = val
return i
}
// 根据类型查找值
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 { // t 为 interface 类型
for k, v := range i.values { // 迭代查询
if k.Implements(t) { // 对于每一个类型,看是否实现了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
}
func (i *injector) SetParent(parent Injector) {
i.parent = parent
}
依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。在传统的程序设计过程中,调用者是自己来决定使用哪些被调用者实现的。但是在依赖注入模式中,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由注入器来完成,然后注入调用者,因此也称为依赖注入。
inject 是依赖注入的golang实现,作者是 codegangsta 。它能在运行时注入参数,调用方法。是Martini框架的基础核心。
我对依赖注入提取了以下2点性质:
由注入器注入属性。
由注入器创建被调用者实例。
在inject中,被调用者为func,因此注入属性也即对func注入实参(当然inject也可以注入struct,这样的话注入的属性就是struct中的已添加tag为inject
的导出字段)。
定义了函数Say并在main方法中手动调用。这样总是可行的,但是有时候我们不得不面对这样一种情况:比如在web开发中,我们注册路由,服务器接受请求,然后根据request path调用相应的handler。这个handler必然不是由我们手动来调用的,而是由服务器端根据路由匹配来查找对应的handler并自动调用。
inject包只有2个文件,一个是inject.go文件,还有一个是inject_test.go,但我们只关注inject.go文件。
inject.go短小精悍,包括注释和空行才157行。定义了4个接口,包括一个父接口和三个子接口,接下来您就会知道这样定义的好处了。
为了方便,我把所有的注释都去掉了:
type Injector interface {
Applicator
Invoker
TypeMapper
SetParent(Injector)
}
type Applicator interface {
Apply(interface{}) error
}
type Invoker interface {
Invoke(interface{}) ([]reflect.Value, error)
}
type TypeMapper interface {
Map(interface{}) TypeMapper
MapTo(interface{}, interface{}) TypeMapper
Get(reflect.Type) reflect.Value
}
接口Injector是接口Applicator、接口Invoker、接口TypeMapper的父接口,所以实现了Injector接口的类型,也必然实现了Applicator接口、Invoker接口和TypeMapper接口。
Applicator接口只规定了Apply成员,它用于注入struct。
Invoker接口只规定了Invoke成员,它用于执行被调用者。
TypeMapper接口规定了三个成员,Map和MapTo都用于注入参数,但它们有不同的用法。Get用于调用时获取被注入的参数。
另外Injector还规定了SetParent行为,它用于设置父Injector,其实它相当于查找继承。也即通过Get方法在获取被注入参数时会一直追溯到parent,这是个递归过程,直到查找到参数或为nil终止。
type injector struct {
values map[reflect.Type]reflect.Value
parent Injector
}
func InterfaceOf(value interface{}) reflect.Type {
t := reflect.TypeOf(value)
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Interface {
panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
}
return t }
func New() Injector {
return &injector{
values: make(map[reflect.Type]reflect.Value),
}
}
injector是inject包中唯一定义的struct,所有的操作都是基于injector struct来进行的。它有两个成员values和parent。values用于保存注入的参数,它是一个用reflect.Type当键、reflect.Value为值的map,这个很重要,理解这点将有助于理解Map和MapTo。New方法用于初始化injector struct,并返回一个指向injector struct的指针。但是请注意这个返回值被Injector接口包装了。
InterfaceOf方法虽然只有几句实现代码,但它是Injector的核心。InterfaceOf方法的参数必须是一个接口类型的指针,如果不是则引发panic。InterfaceOf方法的返回类型是reflect.Type,您应该还记得injector的成员values就是一个reflect.Type类型当键的map。这个方法的作用其实只是获取参数的类型,而不关心它的值。
package main
import (
“fmt”
“github.com/codegangsta/inject”
)
type SpecialString interface{}
func main() {
fmt.Println(inject.InterfaceOf((interface{})(nil)))
fmt.Println(inject.InterfaceOf((SpecialString)(nil)))
}
$ cd $GOPATH/src/injector_test
$ go build
$ ./injector_test
interface {}
main.SpecialString
上面的输出一点也不奇怪。InterfaceOf方法就是用来得到参数类型,而不关心它具体存储的是什么值。值得一提的是,我们定义了一个SpecialString接口。我们在之前的代码也有定义SpecialString接口,用在Say方法的参数声明中,之后您就会知道为什么要这么做。当然您不一定非得命名为SpecialString。
func (i *injector) Map(val interface{}) TypeMapper {
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
return i
}
func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
return i
}
func (i *injector) Get(t reflect.Type) reflect.Value {
val := i.values[t]
if !val.IsValid() && i.parent != nil {
val = i.parent.Get(t)
}
return val
}
func (i *injector) SetParent(parent Injector) {
i.parent = parent
}
Map和MapTo方法都用于注入参数,保存于injector的成员values中。这两个方法的功能完全相同,唯一的区别就是Map方法用参数值本身的类型当键,而MapTo方法有一个额外的参数可以指定特定的类型当键。但是MapTo方法的第二个参数ifacePtr必须是接口指针类型,因为最终ifacePtr会作为InterfaceOf方法的参数。
为什么需要有MapTo方法?因为注入的参数是存储在一个以类型为键的map中,可想而知,当一个函数中有一个以上的参数的类型是一样时,后执行Map进行注入的参数将会覆盖前一个通过Map注入的参数。
SetParent方法用于给某个Injector指定父Injector。Get方法通过reflect.Type从injector的values成员中取出对应的值,它可能会检查是否设置了parent,直到找到或返回无效的值,最后Get方法的返回值会经过IsValid方法的校验。举个例子来加深理解:
package main
import (
“fmt”
“github.com/codegangsta/inject”
“reflect”
)
type SpecialString interface{}
func main() {
inj := inject.New()
inj.Map(“陈一回”)
inj.MapTo(“男”, (SpecialString)(nil))
inj.Map(20)
fmt.Println(“string is valid?”, inj.Get(reflect.TypeOf(“姓陈名一回”)).IsValid())
fmt.Println(“SpecialString is valid?”, inj.Get(inject.InterfaceOf((SpecialString)(nil))).IsValid())
fmt.Println(“int is valid?”, inj.Get(reflect.TypeOf(18)).IsValid())
fmt.Println(“[]byte is valid?”, inj.Get(reflect.TypeOf([]byte(“Golang”))).IsValid())
inj2 := inject.New()
inj2.Map([]byte(“test”))
inj.SetParent(inj2)
fmt.Println(“[]byte is valid?”, inj.Get(reflect.TypeOf([]byte(“Golang”))).IsValid())
}
$ cd $GOPATH/src/injector_test
$ go build
$ ./injector_test
string is valid? true
SpecialString is valid? true
int is valid? true
[]byte is valid? false
[]byte is valid? true
通过以上例子应该知道SetParent是什么样的行为。是不是很像面向对象中的查找链?
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 } Invoke方法用于动态执行函数,当然执行前可以通过Map或MapTo来注入参数,因为通过Invoke执行的函数会取出已注入的参数,然后通过reflect包中的Call方法来调用。Invoke接收的参数f是一个接口类型,但是f的底层类型必须为func,否则会panic。
package main
import (
“fmt”
“github.com/codegangsta/inject”
)
type SpecialString interface{}
func Say(name string, gender SpecialString, age int) {
fmt.Printf(“My name is %s, gender is %s, age is %d!\n”, name, gender, age)
}
func main() {
inj := inject.New()
inj.Map(“陈一回”)
inj.MapTo(“男”, (*SpecialString)(nil))
inj2 := inject.New()
inj2.Map(20)
inj.SetParent(inj2)
inj.Invoke(Say)
}
上面的例子如果没有定义SpecialString接口作为gender参数的类型,而把name和gender都定义为string类型,那么gender会覆盖name的值。如果您还没有明白,建议您把这篇文章从头到尾再看几遍。
func (inj *injector) Apply(val interface{}) error {
v := reflect.ValueOf(val)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil
}
t := v.Type()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
structField := t.Field(i)
if f.CanSet() && structField.Tag == "inject" {
ft := f.Type()
v := inj.Get(ft)
if !v.IsValid() {
return fmt.Errorf("Value not found for type %v", ft)
}
f.Set(v)
}
}
return nil } Apply方法是用于对struct的字段进行注入,参数为指向底层类型为结构体的指针。可注入的前提是:字段必须是导出的(也即字段名以大写字母开头),并且此字段的tag设置为`inject`。以例子来说明:
package main
import (
“fmt”
“github.com/codegangsta/inject”
)
type SpecialString interface{}
type TestStruct struct {
Name string inject
Nick []byte
Gender SpecialString inject
uid int inject
Age int inject
}
func main() {
s := TestStruct{}
inj := inject.New()
inj.Map(“陈一回”)
inj.MapTo(“男”, (*SpecialString)(nil))
inj2 := inject.New()
inj2.Map(20)
inj.SetParent(inj2)
inj.Apply(&s)
fmt.Println(“s.Name =”, s.Name)
fmt.Println(“s.Gender =”, s.Gender)
fmt.Println(“s.Age =”, s.Age)
}
$ cd $GOPATH/src/injector_test
$ go build
$ ./injector_test
s.Name = 陈一回
s.Gender = 男
s.Age = 20