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

2018-08-24  本文已影响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")
}
上一篇下一篇

猜你喜欢

热点阅读