依赖注入依赖注入控制反转(IOC)

一、控制反转是什么?

控制反转即Ioc(后续使用 Ioc 来表述控制反转),Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

1、如何理解 Ioc 呢?

   理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转?什么是正转?反转了是为什么什么?带着疑问往下看。

容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象

2、控制反转和依赖注入

DI—Dependency Injection,即“依赖注入”(后续使用 DI 来表述依赖注入)

   组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
   通过依赖注入机制,我们只需要通过简单的初始化,而无需任何代码就可指定目标依赖对象,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

  1. 谁依赖谁?
       程序依赖 Ioc 容器
  2. 谁注入谁?
       Ioc 容器注入应用程序某个对象,应用程序依赖的对象
  3. 注入了什么?
       注入某个对象所需要的资源

3、小总结

相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

二、依赖注入代码实现

欢迎点赞、使用、提出改进意见:点击跳转 github,下方讲解核心的实现流程。

type LoadFunc func(ILoader) error
var (
	ErrNotFound = errors.New("key not found")
	shared      = NewSingleLoader()
	funcs       = make([]LoadFunc, 0, 16)
)

func DefaultLoader() ILoader {
	return shared
}
type ILoader interface {
	Register(key interface{}, value interface{}) error
	// 加载所有注册变量
	LoadingAll()
	// 加载某结构体下变量
	Loading(v interface{})
}

// 注册 注册方法
func Register(f LoadFunc) {
	funcs = append(funcs, f)
}

// 将 core 的 funcs 保存的注册方法
// 全局注入到 loader 的集合中
func LoadingAll(loader ILoader) (err error) {
	for _, f := range funcs {
		err = f(loader)
		if err != nil {
			return err
		}
	}
	loader.LoadingAll()
	return
}

var _ ILoader = new(singleLoader)

// 初始化单例装载器
func NewSingleLoader() ILoader {
	return &singleLoader{
		objs: make(map[interface{}]reflect.Value),
	}
}

// 全局 Ioc容器
type singleLoader struct {
	objs map[interface{}]reflect.Value
}

func (s *singleLoader) Register(key interface{}, value interface{}) error {
	_, ok := s.objs[key]
	if ok {
		return errors.New(fmt.Sprintf("key duplicate: %v", key))
	}
	s.objs[key] = reflect.ValueOf(value)
	return nil
}
func (s *singleLoader) LoadingAll() {
	for _, v := range s.objs {
		s.Loading(v)
	}
}

// 装载结构体中依赖的字段
func (s *singleLoader) Loading(v interface{}) {
	var value reflect.Value
	var ok bool
	if value, ok = v.(reflect.Value); !ok {
		value = reflect.ValueOf(v)
	}
loop:
	for {
		switch value.Kind() {
		case reflect.Ptr:
			value = value.Elem()
		case reflect.Interface:
			value = value.Elem()
		default:
			break loop
		}
	}

	if value.Kind() != reflect.Struct {
		return
	}
	for i := 0; i < value.NumField(); i++ {
		name := value.Type().Field(i).Tag.Get(LoaderTag)
		temp, ok := s.objs[name]
		if ok {
			field := value.Field(i)
			if field.CanSet() {
				field.Set(temp)
			} else {
				field = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
				field.Set(temp)
			}
		}
	}