前言
你好,我是醉墨居士,欢迎来到我的博客,今天带领大伙使用Go语言实现依赖自动注入,我们不会使用其它的第三方库,项目核心代码不到100行,是Go语言初学者难得的精简项目
依赖注入是什么
依赖注入(Dependency Injection,简称DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC)原则。它的核心思想是将对象的依赖关系从内部管理转移到外部管理,从而降低对象之间的耦合度,提高代码的灵活性和可测试性
依赖注入的好处是什么
降低耦合度:通过将依赖关系从对象内部转移到外部,可以降低对象之间的耦合度。这样,对象只需要知道它需要什么,而不需要知道如何获取这些依赖
提高可测试性:依赖注入使得单元测试变得更加容易。在测试时,可以轻松地替换掉真实的依赖对象,使用模拟对象(Mock Object)或存根(Stub)来进行测试,从而隔离被测试代码
增强可维护性:由于依赖关系被明确地定义和管理,代码的可读性和可维护性得到了提高。当需要修改依赖关系时,只需要在配置或注入点进行修改,而不需要修改对象内部的代码
促进代码重用:依赖注入使得组件可以更容易地在不同的上下文中重用。因为组件不直接创建和管理自己的依赖,所以它们可以在不同的环境中被配置和使用
支持面向接口编程:依赖注入鼓励使用接口来定义依赖关系,而不是具体的实现类。这使得代码更加灵活,因为可以在运行时替换不同的实现,而不需要修改调用代码
简化对象创建:依赖注入容器(如Spring的ApplicationContext)可以自动管理对象的创建和生命周期,开发者不需要手动创建和管理这些对象,从而简化了代码
结构图
现在介绍一下我们依赖注入这个小项目的结构图
应用程序上下文接口
type BeanProvider func() reflect.Value
type ApplicationContext interface {
Inject(provider BeanProvider, name string) error
Autowise(obj any, name string) error
}
上下文管理器
type context struct {
namedConatiner map[string]BeanProvider
typedContainer map[reflect.Type]BeanProvider
}
// 实现依赖注入
func (c *context) Inject(provider BeanProvider, name string) error {
if provider == nil {
return fmt.Errorf("inject: provider can not be nil")
}
if name == "" {
// type inject
ty := provider().Type()
if _, ok := c.typedContainer[ty];ok {
return fmt.Errorf("inject: %v is ambiguous", ty)
}
c.typedContainer[ty] = provider
} else {
// name inject
if _, ok := c.namedConatiner[name];ok {
return fmt.Errorf("inject: %v is ambiguous", name)
}
c.namedConatiner[name] = provider
}
return nil