Go 开源库运行时依赖注入框架 Dependency injection
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的更多相关文章
- 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 ...
- Go语言:一文看懂什么是DI依赖注入(dependency injection)设计模式
前言: 本文主要介绍的是Goalng中关于 DI 的部分,前一部分会先通过典型的面向对象语言Java引入DI这个概念 仅供初学者理解使用,文章如有纰漏敬请指出 本文涉及到的知识面较为零散,其中包含面向 ...
- 清晰架构(Clean Architecture)的Go微服务: 依赖注入(Dependency Injection)
在清晰架构(Clean Architecture)中,应用程序的每一层(用例,数据服务和域模型)仅依赖于其他层的接口而不是具体类型. 在运行时,程序容器¹负责创建具体类型并将它们注入到每个函数中,它使 ...
- 控制反转(Inversion of Control,英文缩写为IoC),另外一个名字叫做依赖注入(Dependency Injection,简称DI)
控制反转(Inversion of Control,英文缩写为IoC),另外一个名字叫做依赖注入(Dependency Injection,简称DI),是一个重要的面向对象编程的法则来削减计算机程序的 ...
- PHP程序员如何理解依赖注入容器(dependency injection container)
背景知识 传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim ...
- Srping - bean的依赖注入(Dependency injection)
目录 1 概述 2 两种基本的依赖注入方式 2.1 构造函数方式 2.2Setter方式 3 其他依赖注入功能 3.1 <ref/>标签引用不同范围的bean 3.2 内部bean 3.3 ...
- ASPNET5 依赖注入(Dependency Injection)
依赖注入一直是asp.net web框架(Web API,SignalR and MVC)中不可或缺的一部分,但是在以前,这个框架都是各自升级,都有各自的依赖注入实现方式,即使Katana项目想通过O ...
- Scalaz(16)- Monad:依赖注入-Dependency Injection By Reader Monad
在上一篇讨论里我们简单的介绍了一下Cake Pattern和Reader Monad是如何实现依赖注入的.主要还是从方法上示范了如何用Cake Pattern和Reader在编程过程中解析依赖和注入依 ...
- 一文读懂Asp.net core 依赖注入(Dependency injection)
一.什么是依赖注入 首先在Asp.net core中是支持依赖注入软件设计模式,或者说依赖注入是asp.net core的核心: 依赖注入(DI)和控制反转(IOC)基本是一个意思,因为说起来谁都离不 ...
- 依赖注入(Dependency Injection)
Spring的两个核心内容为控制反转(Ioc)和面向切面(AOP),依赖注入(DI)是控制反转(Ioc)的一种方式. 依赖注入这个词让人望而生畏,现在已经演变成一项复杂的编程技巧 或设计模式理念.但事 ...
随机推荐
- 制作一个同时具有PE和Windows原版安装方式的U盘
这个方法可能很多人已经制作成功过了,但是呢,也有些人不会的,也可能没想到过,那就是让Win PE与Windows原版安装包在一个U盘里面同时共存. 需要用到的软件有这几样:DiskGenius.Gim ...
- 在进行程序国际化时(Locale)遇到的问题
Java程序国际化-Question 为了使程序能够个根据不同的国家/语言环境来输出不同的内容,通常需要把将需要输出的内容定义在资源文件中. 而在创建资源文件的过程中难免会出现问题,我遇到了在创建资源 ...
- 使用msf生成shellcode并用Go免杀?
msf生成的裸马现在已经不行了,加壳也只能加冷门壳了,VMP,Shielden,upx不是失效就是效果很差,所以当下,得用shellcode来免杀了 msfvenom -a x86 --platfor ...
- ModelAndView方法的返回值类型
一.ModelAndView @RequestMapping("/selectById") public ModelAndView queryById(Integer id){ M ...
- 四月二十二日java基础知识
1.利用接口实现类的多重继承:java语言中接口的主要作用是可以帮助实现类似于类的多重继承功能.多重继承,是指一个子类可以有一个以上的直接父类,该子类可以直接继承它所有父类的非私有成员.2.一个类实现 ...
- layUI之树状表格异步加载组件treetableAsync.js(基于treetable.js)
目录 概述 1. 使用说明 2. 使用需知 2.1 本组件依赖于treetable.js[重中之重] 2.2 本组件基于layUIAdmin进行使用 2.3 本组件的方法支持treetable.js的 ...
- 【LeetCode】3.19 对称二叉树
101. 对称二叉树 给你一个二叉树的根节点 root , 检查它是否轴对称. 示例 1: 输入:root = [1,2,2,3,4,4,3] 输出:true 示例 2: 输入:root = [1 ...
- 【Spring5】数据库事务操作
Spring针对事务的操作 事务的概念:事务是数据库最基本的单元,逻辑上的一组操作,要么都成功,如果有一个操作失败则都失败. 事务的特性:ACID 原子性.一致性.隔离性.持久性 JavaEE环境三层 ...
- 借助 APISIX Ingress,实现与注册中心的无缝集成
作者张晋涛,API7.ai 云原生技术专家,Apache APISIX PMC 成员,Apache APISIX Ingress Controller 项目维护者. 原文链接 云原生场景下是否需要服务 ...
- Dotnet初探: 尝试使用 dotnet6 的miniapi
引子 最近我们学校要求我们使用dotnet实现一个登录功能,由于我们学校的教程老旧(万年经典asp .net 4.x,慢的要死),我看有高性能又免费的Dotnet6不用,还又要退回几年前,于是决定另开 ...