Golang实现简单的IoC

发布时间:2022年02月09日 // 分类:代码 // 暂无评论

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

本文固定链接
https://www.ywlib.com/archives/179.html

标签
golang, ioc

添加新评论 »