架构学习之路(三)-- IOC

魔改谢馒头 · · 1408 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

参考:
https://studygolang.com/articles/13783?fr=sidebar
https://studygolang.com/articles/7716
先写着,我现在吃不透原理
一、IOC
Ioc的思想就是解耦,只依赖容器而不依赖具体的类,当你的类有修改时,最多需要改动一下容器相关代码,业务代码并不受影响。

golang的依赖注入原理

步骤如下:(golang不支持动态创建对象,所以需要先手动创建对象然后注入,java可以直接动态创建对象)

1.通过反射读取对象的依赖(golang是通过tag实现)
2.在容器中查找有无该对象实例
3.如果有该对象实例或者创建对象的工厂方法,则注入对象或使用工厂创建对象并注入
4.如果无该对象实例,则报错

代码实现
package di

import (
    "sync"
    "reflect"
    "fmt"
    "strings"
    "errors"
)

var (
    ErrFactoryNotFound = errors.New("factory not found")
)

type factory = func() (interface{}, error)
// 容器
type Container struct {
    sync.Mutex
    singletons map[string]interface{}
    factories  map[string]factory
}
// 容器实例化
func NewContainer() *Container {
    return &Container{
        singletons: make(map[string]interface{}),
        factories:  make(map[string]factory),
    }
}

// 注册单例对象
func (p *Container) SetSingleton(name string, singleton interface{}) {
    p.Lock()
    p.singletons[name] = singleton
    p.Unlock()
}

// 获取单例对象
func (p *Container) GetSingleton(name string) interface{} {
    return p.singletons[name]
}

// 获取实例对象
func (p *Container) GetPrototype(name string) (interface{}, error) {
    factory, ok := p.factories[name]
    if !ok {
        return nil, ErrFactoryNotFound
    }
    return factory()
}

// 设置实例对象工厂
func (p *Container) SetPrototype(name string, factory factory) {
    p.Lock()
    p.factories[name] = factory
    p.Unlock()
}

// 注入依赖
func (p *Container) Ensure(instance interface{}) error {
    elemType := reflect.TypeOf(instance).Elem()
    ele := reflect.ValueOf(instance).Elem()
    for i := 0; i < elemType.NumField(); i++ { // 遍历字段
        fieldType := elemType.Field(i)
        tag := fieldType.Tag.Get("di") // 获取tag
        diName := p.injectName(tag)
        if diName == "" {
            continue
        }
        var (
            diInstance interface{}
            err        error
        )
        if p.isSingleton(tag) {
            diInstance = p.GetSingleton(diName)
        }
        if p.isPrototype(tag) {
            diInstance, err = p.GetPrototype(diName)
        }
        if err != nil {
            return err
        }
        if diInstance == nil {
            return errors.New(diName + " dependency not found")
        }
        ele.Field(i).Set(reflect.ValueOf(diInstance))
    }
    return nil
}

// 获取需要注入的依赖名称
func (p *Container) injectName(tag string) string {
    tags := strings.Split(tag, ",")
    if len(tags) == 0 {
        return ""
    }
    return tags[0]
}

// 检测是否单例依赖
func (p *Container) isSingleton(tag string) bool {
    tags := strings.Split(tag, ",")
    for _, name := range tags {
        if name == "prototype" {
            return false
        }
    }
    return true
}

// 检测是否实例依赖
func (p *Container) isPrototype(tag string) bool {
    tags := strings.Split(tag, ",")
    for _, name := range tags {
        if name == "prototype" {
            return true
        }
    }
    return false
}

// 打印容器内部实例
func (p *Container) String() string {
    lines := make([]string, 0, len(p.singletons)+len(p.factories)+2)
    lines = append(lines, "singletons:")
    for name, item := range p.singletons {
        line := fmt.Sprintf("  %s: %x %s", name, &item, reflect.TypeOf(item).String())
        lines = append(lines, line)
    }
    lines = append(lines, "factories:")
    for name, item := range p.factories {
        line := fmt.Sprintf("  %s: %x %s", name, &item, reflect.TypeOf(item).String())
        lines = append(lines, line)
    }
    return strings.Join(lines, "\n")
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:魔改谢馒头

查看原文:架构学习之路(三)-- IOC

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1408 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传