依赖注入依赖注入控制反转(IOC)
一、控制反转是什么?
控制反转即Ioc(后续使用 Ioc 来表述控制反转),Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
1、如何理解 Ioc 呢?
理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转?什么是正转?反转了是为什么什么?带着疑问往下看。
容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象
2、控制反转和依赖注入
DI—Dependency Injection,即“依赖注入”(后续使用 DI 来表述依赖注入)
组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。
通过依赖注入机制,我们只需要通过简单的初始化,而无需任何代码就可指定目标依赖对象,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
- 谁依赖谁?
程序依赖 Ioc 容器 - 谁注入谁?
Ioc 容器注入应用程序某个对象,应用程序依赖的对象 - 注入了什么?
注入某个对象所需要的资源
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)
}
}
}