Golang实现简单的IoC

代码8 次浏览

借鉴之前公司同事的思路,简单实现一个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)

相关文章

评论

暂无评论

发表评论

提交后需审核,请勿重复提交。