美文网首页
架构学习之路(三)-- IOC

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

作者: 魔改谢馒头 | 来源:发表于2018-08-24 18:39 被阅读0次

    参考:
    https://studygolang.com/articles/13783?fr=sidebar
    https://studygolang.com/articles/7716
    先写着,我现在吃不透原理
    一、IOC
    Ioc的思想就是解耦,只依赖容器而不依赖具体的类,当你的类有修改时,最多需要改动一下容器相关代码,业务代码并不受影响。
    主要原理通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才决定到底是哪一种对象。
    我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把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

          本文链接:https://www.haomeiwen.com/subject/zakiiftx.html