func fl() {
println ("fl")
}
func f2 () {
println ("f2")
}
funcs := make(map[string] func ())
funcs ["fl"] = fl
funcs ["f2"] = fl
funcs ["fl"]()
funcs ["f2"]()
但是这有个缺陷,就是 map 的 Value 类型被写成 func(),不同参数和返回值的类型的函数并不能通用。将 map 的 Value 定义为 interface{} 空接口类型即可以解决该问题,但需要借助类型断言或反射来实现,通过类型断言实现等于又绕回去了,反射是一种可行的办法。
package main
import (
"fmt"
"github.com/codegangsta/inject"
)
type S1 interface{}
type S2 interface{}
func Format(name string, company S1, level S2, age int) {
fmt.Printf("name = %s, company=%s, level=%s, age = %d!\n", name, company, level, age)
}
func main() {
//控制实例的创建
inj := inject.New()
//实参注入
inj.Map("tom")
inj.MapTo("tencent", (*S1)(nil))
inj.MapTo("T4", (*S2)(nil))
inj.Map(23)
//函数反转调用
inj.Invoke(Format)
}
运行结果如下:
name = tom, company=tencent, level=T4, age = 23!
可见 inject 提供了一种注入参数调用函数的通用功能,inject.New() 相当于创建了一个控制实例,由其来实现对函数的注入调用。inject 包不但提供了对函数的注入,还实现了对 struct 类型的注入,示例代码如下所示:
package main
import (
"fmt"
"github.com/codegangsta/inject"
)
type S1 interface{}
type S2 interface{}
type Staff struct {
Name string `inject`
Company S1 `inject`
Level S2 `inject`
Age int `inject`
}
func main() {
//创建被注入实例
s := Staff{}
//控制实例的创建
inj := inject.New()
//初始化注入值
inj.Map("tom")
inj.MapTo("tencent", (*S1)(nil))
inj.MapTo("T4", (*S2)(nil))
inj.Map(23)
//实现对 struct 注入
inj.Apply(&s)
//打印结果
fmt.Printf("s = %v\n", s)
}
运行结果如下:
s = {tom tencent T4 23}
可以看到 inject 提供了一种对结构类型的通用注入方法。至此,我们仅仅从宏观层面了解 iniect 能做什么,下面从源码实现角度来分析 inject。
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 接口:
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。
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)))
}
运行结果如下:
interface {}
main.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 方法的参数。
package main
import (
"fmt"
"reflect"
"github.com/codegangsta/inject"
)
type SpecialString interface{}
func main() {
inj := inject.New()
inj.Map("C语言中文网")
inj.MapTo("Golang", (*SpecialString)(nil))
inj.Map(20)
fmt.Println("字符串是否有效?", inj.Get(reflect.TypeOf("Go语言入门教程")).IsValid())
fmt.Println("特殊字符串是否有效?", inj.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid())
fmt.Println("int 是否有效?", inj.Get(reflect.TypeOf(18)).IsValid())
fmt.Println("[]byte 是否有效?", inj.Get(reflect.TypeOf([]byte("Golang"))).IsValid())
inj2 := inject.New()
inj2.Map([]byte("test"))
inj.SetParent(inj2)
fmt.Println("[]byte 是否有效?", inj.Get(reflect.TypeOf([]byte("Golang"))).IsValid())
}
运行结果如下所示:
字符串是否有效? true
特殊字符串是否有效? true
int 是否有效? true
[]byte 是否有效? false
[]byte 是否有效? true
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(25)
inj.SetParent(inj2)
inj.Invoke(Say)
}
运行结果如下:
My name is 张三, gender is 男, age is 25!
上面的例子如果没有定义 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(26)
inj.SetParent(inj2)
inj.Apply(&s)
fmt.Println("s.Name =", s.Name)
fmt.Println("s.Gender =", s.Gender)
fmt.Println("s.Age =", s.Age)
}
运行结果如下:
s.Name = 张三
s.Gender = 男
s.Age = 26
版权说明:Copyright © 广州松河信息科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州松河信息科技有限公司 版权所有