Golang轻量级IoC容器安装使用示例
作者:深蓝studyzy 发布时间:2023-07-23 14:49:12
1. iocgo简介
习惯于Java或者C#开发的人应该对控制反转与依赖注入应该再熟悉不过了。在Java平台有鼎鼎大名的Spring框架,在C#平台有Autofac,Unity,Windsor等,我当年C#开发时用的最多的就是Windsor。使用IoC容器是面向对象开发中非常方便的解耦模块之间的依赖的方法。各个模块之间不依赖于实现,而是依赖于接口,然后在构造函数或者属性或者方法中注入特定的实现,方便了各个模块的拆分以及模块的独立单元测试。
在[长安链]的设计中,各个模块可以灵活组装,模块之间的依赖基于protocol中定义的接口,每个接口有一个或者多个官方实现,当然第三方也可以提供该接口更多的实现。为了实现更灵活的组装各个模块,管理各个模块的依赖关系,于是我写了iocgo这个轻量级的golang版Ioc容器。
2. iocgo如何使用
2.1 iocgo包的安装
现在go官方版本已经出到1.17了,当然我在代码中其实也没有用什么新版本的新特性,于是就用1.15版本或者之后的Go版本即可。要使用iocgo包,直接通过go get添加到项目中:
go get github.com/studyzy/iocgo
2.2 使用示例与说明
2.2.1 最简单的例子:
type Fooer interface {
Foo(int)
}
type Foo struct {
}
func (Foo)Foo(i int) {
fmt.Println("foo:",i)
}
type Barer interface {
Bar(string)
}
type Bar struct {
}
func (Bar) Bar(s string){
fmt.Println("bar:",s)
}
type Foobarer interface {
Say(int,string)
}
type Foobar struct {
foo Fooer
bar Barer
}
func NewFoobar(f Fooer,b Barer) Foobarer{
return &Foobar{
foo: f,
bar: b,
}
}
func (f Foobar)Say(i int ,s string) {
f.foo.Foo(i)
f.bar.Bar(s)
}
func TestContainer_SimpleRegister(t *testing.T) {
container := NewContainer()
container.Register(NewFoobar)
container.Register(func() Fooer { return &Foo{} })
container.Register(func() Barer { return &Bar{} })
var fb Foobarer
container.Resolve(&fb)
fb.Say(123,"Hello World")
}
这里我使用NewContainer()创建了一个新的容器,然后在容器中调用Register方法注册了3个接口和对应的构造函数,分别是:
Foobarer接口对应NewFoobar(f Fooer,b Barer)构造函数
Fooer接口对应构造&Foo{}的匿名函数。
Barer接口对应构造&Bar{}的匿名函数。
接下来调用Resolve函数,并传入var fb Foobarer 这个接口变量的指针,iocgo就会自动去构建Foobarer对应的实例,并最终将实例赋值到fb这个变量上,于是最后我们就可以正常调用fb.Say实例方法了。
2.22. Register 的选项
iocgo的注册interface到对象的函数定义如下:
func Register(constructor interface{}, options ...Option) error
iocgo为Register函数提供了以下参数选项可根据实际情况选择性使用:
Name 为某个interface->对象的映射命名
Optional 表名这个构造函数中哪些注入的interface参数是可选的,如果是可选,那么就算找不到interface对应的实例也不会报错。
Interface 显式声明这个构造函数返回的实例是映射到哪个interface。
Lifestyle(isTransient) 声明这个构造函数在构造实例后是构造的临时实例还是单例实例,如果是临时实例,那么下次再获取该interface对应的实例时需要再次调用构造函数,如果是单例,那么就缓存实例到容器中,下次再想获得interface对应的实例时直接使用缓存中的,不需要再次构造。
DependsOn 这个主要是指定构造函数中的某个参数在通过容器获得对应的实例时,应该通过哪个Name去获得对应的实例。
Parameters 这个主要用于指定构造函数中的某些非容器托管的参数,比如某构造函数中有int,string等参数,而这些参数的实例是不需要通过ioc容器进行映射托管的,那么就在这里直接指定。
Default 这个主要用于设置一个interface对应的默认的实例,也就是如果没有指定Name的情况下,应该找哪个实例。 关于每一个参数该如何使用,我都写了UT样例,具体参考: container_test.go
2.2.3. 注册实例
如果我们已经有了某个对象的实例,那么可以将该实例和其想映射的interface直接注册到ioc容器中,方便其他依赖的对象获取,RegisterInstance函数定义如下:
RegisterInstance(interfacePtr interface{}, instance interface{}, options ...Option) error
使用上也很简单,直接将实例对应的interface的指针作为参数1,实例本身作为参数2,传入RegisterInstance即可:
b := &Bar{}
var bar Barer //interface
container.RegisterInstance(&bar, b) // register interface -> instance
2.2.4. 获得实例
相关映射我们通过Register函数和RegisterInstance函数已经注册到容器中,接下来就需要从容器获得指定的实例了。获得实例需要调用函数:
func Resolve(abstraction interface{}, options ...ResolveOption) error
这里第一个参数abstraction是我们想要获取的某个interface的指针,第二个参数是可选参数,目前提供的选项有:
ResolveName 指定使用哪个name的interface和实例的映射,如果不指定,那么就是默认映射。
Arguments 指定在调用对应的构造函数获得实例时,传递的参数,比如int,string等类型的不在ioc容器中托管的参数,可以在这里指定。如果构造函数本身需要这些参数,而且在前面Register的时候已经通过Parameters选项进行了指定,那么这里新的指定会覆盖原有Register的指定。
var fb Foobarer
err:=container.Resolve(&fb)
另外如果我们的构造函数return的值中支持error,而且实际构造的时候确实返回了error,那么Resolve函数也会返回对应的这个err。
特别注意:Resolve的第一个参数是申明的某个interface的指针,一定要是指针,不能直接传interface
2.2.5. 结构体参数和字段填充
有些时候构造函数的入参非常多,于是我们可以申明一个结构体,把所有入参都放入这个结构体中,这样构造函数就只需要一个参数了。iocgo也支持自动填充这个结构体中interface对应的实例,从而构造新的对象。另外iocgo也提供了Fill方法,可以直接填充某个结构体,比如:
type FoobarInput struct {
foo Fooer
bar Barer
msg string
}
input := FoobarInput{
msg: "studyzy",
}
container.Register(func() Fooer { return &Foo{} })
container.Register(func() Barer { return &Bar{} })
err := container.Fill(&input)
结构体中的字段还支持tag,目前提供的tag有两种:
name //指定这个字段在获得对应的实例时使用的name
optional //指定这个字段是否是可选的,如果是,那么就算获得不到对应的实例,也不会报错。 示例example:
type FoobarInputWithTag struct {
foo Fooer `optional:"true"`
bar Barer `name:"baz"`
msg string
}
2.2.6. 函数调用
除了构造函数注入之外,iocgo也支持函数注入,我们申明一个函数,这个函数的参数中有些参数是interface,那么通过调用iocgo中的Call方法,可以为这个函数注入对应的实例作为参数,并最终完成函数的调用。 示例 example:
func SayHi1(f Fooer, b Barer) {
f.Foo(1234)
b.Bar("hi")
}
Register(func() Fooer { return &Foo{} })
Register(func() Barer { return &Bar{} })
Call(SayHi1)
Call函数也是支持选项的,目前提供了2个选项:
CallArguments 指定函数中某个参数的值
CallDependsOn 指定函数中某个参数在通过ioc容器获得实例时使用哪个name来获得实例。 最后函数调用完成,如果函数本身有多个返回值,有error返回,那么Call函数也会返回对应的结果。
2.3 参考:
在写这个iocgo的代码时,主要参考了以下两个Ioc相关的项目:
https://github.com/golobby/container
https://github.com/castleproject/Windsor
3. 总结
iocgo是一个纯Golang语言开发的用于管理依赖注入的IoC容器,使用这个容器可以很好的实现go语言下的面向对象开发,模块解耦。现已经开源,欢迎大家使用,开源地址:https://github.com/studyzy/iocgo
来源:https://cloud.tencent.com/developer/article/2025058
猜你喜欢
- 得益于 Python 的自动垃圾回收机制,在 Python 中创建对象时无须手动释放。这对开发者非常
- 如下所示:<!doctype html><html><head><meta charset=&qu
- 本文实例讲述了python引用DLL文件的方法。分享给大家供大家参考。具体分析如下:在python中调用dll文件中的接口比较简单,如我们有
- 本文实例为大家分享了python3.5 email发送邮件的具体代码,供大家参考,具体内容如下直接套用代码即可from email.mime
- 简述公司使用gitlab 来托管代码,日常代码merge request 以及其他管理是交给测试,鉴于操作需经常打开网页,重复且繁琐,所以交
- 构建网络ResNet由一系列堆叠的残差块组成,其主要作用是通过无限制地增加网络深度,从而使其更加强大。在建立ResNet模型之前,让我们先定
- 1、最小二乘也可以拟合二次函数我们都知道用最小二乘拟合线性函数没有问题,那么能不能拟合二次函数甚至更高次的函数呢?答案当然是可以的。下面我们
- 本文实例讲述了MySQL数据库优化之分表分库操作。分享给大家供大家参考,具体如下:分表分库垂直拆分垂直拆分就是要把表按模块划分到不同数据库表
- 本文实例讲述了JS获取日期的方法。分享给大家供大家参考,具体如下:原理很简单,一天的时间的毫秒数是1000*60*60*24,前n天的日期就
- 下面先说说window.showModalDialog的基本用法showModalDialog() (IE 4+ 支持)showModele
- For 循环可以遍历字符串,也可以遍历列表# for 循环# 语法特点:遍历操作,依次取集合容器中的几个值# for 临时变量
- 前言在机器学习中,我们会经常和矩阵打交道。在矩阵的运算中,python默认的输出是浮点数,但是如果我们想要矩阵的元素以分数的形式显示,可以通
- Stickyworld 的网页应用已经支持视频拨放一段时间,但都是通过YouTube的嵌入模式实现。我们开始提供新的版本支持视频操作,可以让
- pycharm的pytest功能在新建一个python文件时,比如名称是test_test_test.py,由于含有test,pycharm
- asp之家注:作为一个学习asp的爱好者,相信一定接触过session,我们经常使用session来作为会员登录的验证,当然也可以使用COO
- 需求:用户输入运算表达式,终端显示计算结果代码:# !/usr/bin/env/ python3# -*- coding: utf-8 -*
- 从matplotlib工具栏源码探析一(禁用工具栏、默认工具栏和工具栏管理器三种模式的差异)一文可知matplotlib内置实现了多个工具项
- 继上一篇单向链表,单线链表可以进一步扩展为环,如下图所示:特点:1、第一个节点称为头部节点,最后一个节点称为尾部节点2、每个节点都单方面的指
- AES(英文:Advanced Encryption Standard,中文:高级加密标准),是一种区块加密标准。AES将原始数
- 1 俄罗斯方块游戏《俄罗斯方块》原本是前苏联科学家阿列克谢·帕基特诺夫所开发的教育用软件,之后开始提供授权给各个游戏公