Dependency injection

一个Go编程语言的运行依赖注入库。依赖注入是更广泛的控制反转技术的一种形式。它用于增加程序的模块化并使其具有可扩展性。

实例展示(High API):

type A struct {
Name string
} func NewA() *A {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
name := "A-" + strconv.Itoa(r.Int())
return &A{Name: ls}
} services := NewServiceCollection()
services.AddSingleton(NewA)
//serviceCollection.AddSingletonByImplementsAndName("redis-master", NewRedis, new(abstractions.IDataSource))
//serviceCollection.AddTransientByImplements(NewRedisClient, new(redis.IClient))
//serviceCollection.AddTransientByImplements(NewRedisHealthIndicator, new(health.Indicator))
serviceProvider := services.Build() var env *A
_ = serviceProvider.GetService(&env) // used

依赖注入将如何帮助我们的?

依赖注入是更广泛的控制反转技术的一种形式。它用于增加程序的模块化并使其具有可扩展性。

安装

go get -u github.com/yoyofxteam/dependencyinjection@v1.0.1

组件特性

  • Providing
  • Extraction
  • Invocation
  • Lazy-loading
  • Interfaces
  • Groups
  • Named definitions
  • Optional parameters
  • Parameter Bag
  • Prototypes
  • Cleanup

Providing

首先,我们需要创建两个基本类型:http。服务器和http.ServeMux。让我们创建一个简单的构造函数来初始化它:

// NewServer creates a http server with provided mux as handler.
func NewServer(mux *http.ServeMux) *http.Server {
return &http.Server{
Handler: mux,
}
} // NewServeMux creates a new http serve mux.
func NewServeMux() *http.ServeMux {
return &http.ServeMux{}
}

支持的构造器签名形式如下:

func([dep1, dep2, depN]) (result, [cleanup, error])

现在让我们来容器构建这些类型。

下面展示是Low API, 也可以用High API来构建:

import (
di "github.com/yoyofxteam/dependencyinjection"
) container := di.New(
// provide http server
di.Provide(NewServer),
// provide http serve mux
di.Provide(NewServeMux)
)

函数di. new()解析构造函数,编译依赖关系图并返回*di。用于交互的容器类型。如果无法编译,容器会出现恐慌。

我认为在应用程序初始化时而不是在运行时出现恐慌是很常见的。

Extraction

我们可以从容器中提取构建的服务器。为此,定义提取类型的变量,并将变量指针传递给Extract函数。

如果未找到提取的类型或构建实例的过程导致错误,则提取返回错误。

如果没有错误发生,我们就可以使用这个变量,就像我们自己构建它一样。

// declare type variable
var server *http.Server
// extracting
err := container.Extract(&server)
if err != nil {
// check extraction error
} server.ListenAndServe()

请注意,默认情况下,容器作为单例创建实例 , 但也可使用Provide做行为上的改变。


## Invocation
作为提取的替代方法,我们可以使用Invoke()函数。它解析函数依赖并调用函数。调用函数可能返回可选错误。
```golang
// NewServer creates a http server with provided mux as handler.
func NewServer(handler http.Handler) *http.Server {
return &http.Server{
Handler: handler,
}
}

对于一个容器来说,要知道作为http的实现。处理程序是必须使用的,我们使用选项di.As()。这个选项的参数必须是一个指向接口的指针,比如new(Endpoint)。

这种语法可能看起来很奇怪,但我还没有找到更好的方式来指定接口。

修改依赖注入容器初始化代码:

container := inject.New(
// provide http server
inject.Provide(NewServer),
// provide http serve mux as http.Handler interface
inject.Provide(NewServeMux, inject.As(new(http.Handler)))
)

现在容器使用提供*http。ServeMux作为http。服务器构造函数中的处理程序。使用接口有助于编写更多可测试的代码。

Groups

容器自动将接口的所有实现分组到[]组。例如,提供with inject.As(new(http.Handler))自动创建一个组[]http.Handler。

让我们使用这个特性添加一些http控制器。控制器有典型的行为。它正在注册路由。首先,将为它创建一个接口。

// Controller is an interface that can register its routes.
type Controller interface {
RegisterRoutes(mux *http.ServeMux)
}

现在我们将编写控制器并实现控制器接口。

// OrderController is a http controller for orders.
type OrderController struct {} // NewOrderController creates a auth http controller.
func NewOrderController() *OrderController {
return &OrderController{}
} // RegisterRoutes is a Controller interface implementation.
func (a *OrderController) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("/orders", a.RetrieveOrders)
} // Retrieve loads orders and writes it to the writer.
func (a *OrderController) RetrieveOrders(writer http.ResponseWriter, request *http.Request) {
// implementation
}

UserController

// UserController is a http endpoint for a user.
type UserController struct {} // NewUserController creates a user http endpoint.
func NewUserController() *UserController {
return &UserController{}
} // RegisterRoutes is a Controller interface implementation.
func (e *UserController) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("/users", e.RetrieveUsers)
} // Retrieve loads users and writes it using the writer.
func (e *UserController) RetrieveUsers(writer http.ResponseWriter, request *http.Request) {
// implementation
}

就像在这个接口的例子中,我们将使用inject.As()提供provide选项。

container := inject.New(
di.Provide(NewServer), // provide http server
di.Provide(NewServeMux), // provide http serve mux
// endpoints
di.Provide(NewOrderController, di.As(new(Controller))), // provide order controller
di.Provide(NewUserController, di.As(new(Controller))), // provide user controller
)

现在,我们可以在mux中使用控制器数组。参见更新后的代码:

// NewServeMux creates a new http serve mux.
func NewServeMux(controllers []Controller) *http.ServeMux {
mux := &http.ServeMux{} for _, controller := range controllers {
controller.RegisterRoutes(mux)
} return mux
}

高级功能

Named definitions

在某些情况下,一种类型有多个实例。例如两个数据库实例:master -用于写,slave -用于读。

// MasterDatabase provide write database access.
type MasterDatabase struct {
*Database
} // SlaveDatabase provide read database access.
type SlaveDatabase struct {
*Database
}

第二种方法是使用带有di.WithName()提供选项的命名定义

// provide master database
di.Provide(NewMasterDatabase, di.WithName("master"))
// provide slave database
di.Provide(NewSlaveDatabase, di.WithName("slave"))

如果需要从容器中提取,请使用di.Name()提取选项。

var db *Database
container.Extract(&db, di.Name("master"))

如果需要在其他构造函数中提供命名定义,请使用di。参数嵌入。

// ServiceParameters
type ServiceParameters struct {
di.Parameter // use `di` tag for the container to know that field need to be injected.
MasterDatabase *Database `di:"master"`
SlaveDatabase *Database `di:"slave"`
} // NewService creates new service with provided parameters.
func NewService(parameters ServiceParameters) *Service {
return &Service{
MasterDatabase: parameters.MasterDatabase,
SlaveDatabase: parameters.SlaveDatabase,
}
}

Optional parameters

// ServiceParameter
type ServiceParameter struct {
di.Parameter StdOutLogger *Logger `di:"stdout"`
FileLogger *Logger `di:"file,optional"`
}

将依赖声明为可选的构造函数必须处理这些依赖不存在的情况。

Parameter Bag

如果需要在定义级别上指定一些参数,则可以使用 inject.ParameterBag provide option. 这是一个map[string]interface{},可以转换为di.ParameterBag类型。

// Provide server with parameter bag
di.Provide(NewServer, di.ParameterBag{
"addr": ":8080",
}) // NewServer create a server with provided parameter bag. Note: use di.ParameterBag type.
// Not inject.ParameterBag.
func NewServer(pb di.ParameterBag) *http.Server {
return &http.Server{
Addr: pb.RequireString("addr"),
}
}

Prototypes

如果您想在每次提取上创建一个新实例,请使用di.Prototype()提供选项。

di.Provide(NewRequestContext, di.Prototype())

Cleanup

如果提供程序创建了一个需要清理的值,那么它可以返回一个闭包来清理资源。

func NewFile(log Logger, path Path) (*os.File, func(), error) {
f, err := os.Open(string(path))
if err != nil {
return nil, nil, err
}
cleanup := func() {
if err := f.Close(); err != nil {
log.Log(err)
}
}
return f, cleanup, nil
}

在调用container.Cleanup()之后,它遍历实例,如果存在则调用cleanup函数。

container := di.New(
// ...
di.Provide(NewFile),
) // do something
container.Cleanup() // file was closed

总结

Dependency injection 是一个运行时的依赖注入框架, 它也会集成到了微服务框架 yoyogo 中. YoyoGo (Go语言框架)一个简单、轻量、快速、基于依赖注入的微服务框架( web 、grpc ),支持Nacos/Consoul/Etcd/Eureka/k8s /Apollo等 .

参考:

yoyogo: https://github.com/yoyofx/yoyogo

dependencyinjection: https://github.com/yoyofxteam/dependencyinjection

Go 开源库运行时依赖注入框架 Dependency injection的更多相关文章

  1. Compile-time Dependency Injection With Go Cloud's Wire 编译时依赖注入 运行时依赖注入

    Compile-time Dependency Injection With Go Cloud's Wire - The Go Blog https://blog.golang.org/wire Co ...

  2. Go语言:一文看懂什么是DI依赖注入(dependency injection)设计模式

    前言: 本文主要介绍的是Goalng中关于 DI 的部分,前一部分会先通过典型的面向对象语言Java引入DI这个概念 仅供初学者理解使用,文章如有纰漏敬请指出 本文涉及到的知识面较为零散,其中包含面向 ...

  3. 清晰架构(Clean Architecture)的Go微服务: 依赖注入(Dependency Injection)

    在清晰架构(Clean Architecture)中,应用程序的每一层(用例,数据服务和域模型)仅依赖于其他层的接口而不是具体类型. 在运行时,程序容器¹负责创建具体类型并将它们注入到每个函数中,它使 ...

  4. 控制反转(Inversion of Control,英文缩写为IoC),另外一个名字叫做依赖注入(Dependency Injection,简称DI)

    控制反转(Inversion of Control,英文缩写为IoC),另外一个名字叫做依赖注入(Dependency Injection,简称DI),是一个重要的面向对象编程的法则来削减计算机程序的 ...

  5. PHP程序员如何理解依赖注入容器(dependency injection container)

    背景知识 传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim ...

  6. Srping - bean的依赖注入(Dependency injection)

    目录 1 概述 2 两种基本的依赖注入方式 2.1 构造函数方式 2.2Setter方式 3 其他依赖注入功能 3.1 <ref/>标签引用不同范围的bean 3.2 内部bean 3.3 ...

  7. ASPNET5 依赖注入(Dependency Injection)

    依赖注入一直是asp.net web框架(Web API,SignalR and MVC)中不可或缺的一部分,但是在以前,这个框架都是各自升级,都有各自的依赖注入实现方式,即使Katana项目想通过O ...

  8. Scalaz(16)- Monad:依赖注入-Dependency Injection By Reader Monad

    在上一篇讨论里我们简单的介绍了一下Cake Pattern和Reader Monad是如何实现依赖注入的.主要还是从方法上示范了如何用Cake Pattern和Reader在编程过程中解析依赖和注入依 ...

  9. 一文读懂Asp.net core 依赖注入(Dependency injection)

    一.什么是依赖注入 首先在Asp.net core中是支持依赖注入软件设计模式,或者说依赖注入是asp.net core的核心: 依赖注入(DI)和控制反转(IOC)基本是一个意思,因为说起来谁都离不 ...

  10. 依赖注入(Dependency Injection)

    Spring的两个核心内容为控制反转(Ioc)和面向切面(AOP),依赖注入(DI)是控制反转(Ioc)的一种方式. 依赖注入这个词让人望而生畏,现在已经演变成一项复杂的编程技巧 或设计模式理念.但事 ...

随机推荐

  1. 计算机网络中各种报文、HEADER的读法

    计算机里到处都是格式,规范.比如<操作系统真象还原>里提到的"魔数" 直接出现的一个数字,只要其意义不明确,感觉很诡异,就称之为魔数. 拿elf文件头举例 ELF He ...

  2. Pycharm 搭建 Django 项目

    1. 安装需求 在使用 python 框架 Django 需要注意下面事项 Pycharm 版本是专业版而不是社区版本 Pycharm 配置好了 python 解释器 (一般我们现在用的都是pytho ...

  3. SpringBoot——配置及原理

    更多内容,前往IT-BLOG 一.Spring Boot全局配置文件 application.properties 与 application.yml 配置文件的作用:可以覆盖 SpringBoot ...

  4. Three.js 进阶之旅:全景漫游-初阶移动相机版

    声明:本文涉及图文和模型素材仅用于个人学习.研究和欣赏,请勿二次修改.非法传播.转载.出版.商用.及进行其他获利行为. 摘要 3D 全景技术可以实现日常生活中的很多功能需求,比如地图的街景全景模式.数 ...

  5. EX6新增的字符串

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 用 Go 剑指 Offer 12. 矩阵中的路径

    给定一个 m x n 二维字符网格 board 和一个字符串单词 word .如果 word 存在于网格中,返回 true :否则,返回 false . 单词必须按照字母顺序,通过相邻的单元格内的字母 ...

  7. vue事件监听

    v-on <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  8. SRE中的SLA/SLO/SLI

    SLA通俗理解 SLA 表征服务方与客户间的服务等级协议,定义服务方需保证的服务质量以及不达标情况下的服务补偿,在SRE领域,SLA 细分为 SLI.SLO 与 SLA: SLI,服务质量指标,服务的 ...

  9. javasec(三)类加载机制

    这篇文章介绍java的类加载机制. Java是一个依赖于JVM(Java虚拟机)实现的跨平台的开发语言.Java程序在运行前需要先编译成class文件,Java类初始化的时候会调用java.lang. ...

  10. javasec(六)RMI

    这篇文章介绍java-RMI远程方法调用机制. RMI全称是Remote Method Invocation,远程⽅法调⽤.是让某个Java虚拟机上的对象调⽤另⼀个Java虚拟机中对象上的⽅法,只不过 ...