借鉴之前公司同事的思路,简单实现一个IoC练练手。
package ioc
import (
"fmt"
"reflect"
)
var container map[reflect.Type]map[string]reflect.Value
func init() {
container = map[reflect.Type]map[string]reflect.Value{}
}
const (
DefaultName = ""
)
// Set is 注册IoC实例
func Set(interfaceObj interface{}, instanceObj interface{}) {
SetWithName(interfaceObj, instanceObj, DefaultName)
}
// SetWithName is 注册IoC实例并指定该实例名称(允许存在不同名称的同类实例)
func SetWithName(interfaceObj interface{}, instanceObj interface{}, name string) {
assertInstanceImplementsInterface(interfaceObj, instanceObj)
interfaceElem := fetchInterfaceElem(interfaceObj)
if container[interfaceElem] == nil {
container[interfaceElem] = map[string]reflect.Value{}
}
container[interfaceElem][name] = reflect.ValueOf(instanceObj)
}
// Get is 获取已注册的实例
func Get(interfaceObj interface{}) interface{} {
return GetWithName(interfaceObj, DefaultName)
}
// GetWithName is 获取指定名称的实例
func GetWithName(interfaceObj interface{}, name string) interface{} {
interfaceElem := fetchInterfaceElem(interfaceObj)
if _, ok := container[interfaceElem]; !ok {
panic(fmt.Errorf("ioc: the instance of interface <%s> is not exit", interfaceElem.String()))
}
if _, ok := container[interfaceElem][name]; !ok {
if name == DefaultName {
panic(fmt.Errorf("ioc: the instance of interface <%s> with default is not exit", interfaceElem.String()))
} else {
panic(fmt.Errorf("ioc: the instance of interface <%s> with name <%s> is not exit", interfaceElem.String(), name))
}
}
return container[interfaceElem][name].Interface()
}
// Inject is 自动将实例注入到目标对象中
func Inject(targetObj interface{}) {
targetType := reflect.TypeOf(targetObj)
if targetType.Kind() != reflect.Ptr {
panic(fmt.Errorf("ioc: inject target <%s> is not ptr", targetType.String()))
}
targetElemType := targetType.Elem()
targetElemValue := reflect.ValueOf(targetObj).Elem()
targetFieldCount := targetElemType.NumField()
for i := 0; i < targetFieldCount; i++ {
fieldType := targetElemType.Field(i)
name, ok := fieldType.Tag.Lookup("inject")
if !ok {
continue
}
interfaceElem := fieldType.Type
if _, ok := container[interfaceElem]; !ok {
panic(fmt.Errorf("ioc: interface <%s> injecting has not set", interfaceElem.String()))
}
if _, ok := container[interfaceElem][name]; !ok {
if name == DefaultName {
panic(fmt.Errorf("ioc: interface <%s> default injecting has not set", interfaceElem.String()))
} else {
panic(fmt.Errorf("ioc: interface <%s> with name <%s> injecting has not set", interfaceElem.String(), name))
}
}
targetElemValue.FieldByIndex(fieldType.Index).Set(container[interfaceElem][name])
}
}
// ---
func assertInstanceImplementsInterface(interfaceObj interface{}, instanceObj interface{}) {
interfaceElemType := fetchInterfaceElem(interfaceObj)
instanceType := reflect.TypeOf(instanceObj)
if !instanceType.Implements(interfaceElemType) {
panic(fmt.Errorf(
"ioc: the instance <%s> do not implement interface <%s>",
instanceType.String(),
interfaceElemType.String(),
))
}
}
func fetchInterfaceElem(interfaceObj interface{}) reflect.Type {
interfaceType := reflect.TypeOf(interfaceObj)
if interfaceType.Kind() != reflect.Ptr {
panic(fmt.Errorf("ioc: the interface object <%s> is not ptr", interfaceType.String()))
}
return interfaceType.Elem()
}
简单使用
初始化
initioc.InitIOC()
注册
ioc.Set(
new(contract.IEnvService),
envSvc,
)
ioc.SetWithName(
new(contract.IEnvService),
envSvc,
"SpecialEnv",
)
获取
envSvc := ioc.Get(new(contract.IEnvService)).(contract.IEnvService)
envSvc := ioc.GetWithName(new(contract.IEnvService),"SpecialEnv").(contract.IEnvService)
自动注入
container := struct {
EnvSvc contract.IEnvService`inject:""`
}{}
ioc.Inject(&container)
转载请注明出处
《Golang实现简单的IoC》https://www.ywlib.com/archives/179.html (from 一闻自习室)