Go-IOC 是一款为Go语言开发的运行时依赖注入库。Go语言的语言特性决定了实现一款类型安全的依赖注入容器并不太容易,因此 Go-IOC 大量使用了Go的反射机制。如果你的使用场景对性能要求并不是那个苛刻,那 Go-IOC 非常适合你。
并不是说对性能要求苛刻的环境中就不能使用了,你可以把 Go-IOC 作为一个对象依赖管理工具,在你的业务初始化时获取依赖的对象。
使用方式gogetgithub.com/mylxsw/go-ioc要创建一个 Cotaier 实例,使用 ioc.New 方法cc:=ioc.New()此时就创建了一个空的容器。你也可以使用 ioc.NewWithCotext(ctx) 来创建容器,创建之后,可以自动的把已经存在的 cotext.Cotext 对象添加到容器中,由容器托管。
对象绑定在使用之前,我们需要先将我们要托管的对象告诉容器。Cotaier 支持三种类型的对象管理单例对象 Sigleto原型对象(多例对象) Prototype字符串值对象绑定 Value所有的对象绑定方法都会返回一个 error 返回值来说明是否绑定成功,应用在使用时一定要主动去检查这个 error。
确定对象一定会绑定成功(一般不违反文档中描述的参数签名方式,都是一定会成功的)或者要求对象必须要绑定成功(通常我们都要求这样,不然怎么进行依赖管理呢),则可以使用 Must 系列方法,比如 Sigleto 方法对应的时 MustSigleto,当创建出错时,该方法会直接 paic。
绑定对象时,Sigleto,Prototype,BidValue 方法对于同一类型,只能绑定一次,如果多次绑定同一类型对象的创建函数,会返回 ErrRepeatedBid 错误。有时候,希望对象创建函数可以多次重新绑定,这样就可以个应用更多的扩展性,可以随时替换掉对象的创建方法,比如测试时 Mock 对象的注入。这时候我们可以使用 Override 系列方法:SigletoOverridePrototypeOverrideBidValueOverride使用 Override 系列方法时,必须保证第一次绑定时使用的是 Override 系列方法,否则无法重新绑定。也就是说,可以这样绑定 SigletoOverride -> SigletoOverride ,SigletoOverride -> Sigleto,但是一旦出现 Sigleto,后续就无法对该对象重新绑定了。
单例对象使用 Sigleto 系列的方法来将单例对象托管给容器,单例对象只会在第一次使用时自动完成创建,之后所有对该对象的访问都会自动将已经创建好的对象注入进来。常用的方法是 Sigleto(iitializeiterface{})error 方法,该方法会按照你提供的 iitialize 函数或者对象来完成单例对象的注册。参数 iitialize 支持以下几种形式:对象创建函数 fuc(deps...)对象返回值
比如
cc.Sigleto(fuc()UserRepo{retur&userRepoImpl{}})cc.Sigleto(fuc()(*sql.DB,error){retursql.Ope("mysql","user:pwd@tcp(ip:3306)/dbame")})cc.Sigleto(fuc(db*sql.DB)UserRepo{//这里我们创建的userRepoImpl对象,依赖sql.DB对象,只需要在函数//参数中,将依赖列举出来,容器会自动完成这些对象的创建retur&userRepoImpl{db:db}})带错误返回值的对象创建函数 fuc(deps...)(对象返回值,error)
对象创建函数最多支持两个返回值,且要求第一个返回值为期望创建的对象,第二个返回值为error对象。
cc.Sigleto(fuc()(Cofig,error){//假设我们要创建配置对象,该对象的初始化时从文件读取配置cotet,err:=ioutil.ReadFile("test.cof")iferr!=il{returil,err}returcofig.Load(cotet),il})直接绑定对象
如果对象已经创建好了,想要让 Cotaier 来管理,可以直接将对象传递 Sigleto 方法
userRepo:=repo.NewUserRepo()cc.Sigleto(userRepo)当对象第一次被使用时,Cotaier 会将对象创建函数的执行结果缓存起来,从而实现任何时候后访问都是获取到的同一个对象。
原型对象(多例对象)原型对象(多例对象)是指的由 Cotaier 托管对象的创建过程,但是每次使用依赖注入获取到的都是新创建的对象。使用 Prototype 系列的方法来将原型对象的创建托管给容器。常用的方法是 Prototype(iitializeiterface{})error。参数 iitialize 可以接受的类型与 Sigleto 系列函数完全一致,唯一的区别是在对象使用时,单例对象每次都是返回的同一个对象,而原型对象则是每次都返回新创建的对象。字符串值对象绑定这种绑定方式是将某个对象绑定到 Cotaier 中,但是与 Sigleto 系列方法不同的是,它要求必须指定一个字符串类型的 Key,每次获取对象的时候,使用 Get 系列函数获取绑定的对象时,直接传递这个字符串Key即可。常用的绑定方法为 BidValue(keystrig,valueiterface{})。cc.BidValue("versio","1.0.1")cc.MustBidValue("startTs",time.Now())cc.BidValue("it_val",123)依赖注入在使用绑定对象时,通常我们使用 Resolve 和 Call 系列方法。ResolveResolve(callbackiterface{})error 方法执行体callback内部能够进行依赖注入,error 返回值,表明在注入对象时产生错误或者callback返回了error。比如,我们需要获取某个用户的信息和其角色信息,使用Resolve方法cc.MustResolve(fuc(userReporepo.UserRepo,roleReporepo.RoleRepo){//查询id=123的用户,查询失败直接paicuser,err:=userRepo.GetUser(123)iferr!=il{paic(err)}//查询用户角色,查询失败时,我们忽略了返回的错误role,_:=roleRepo.GetRole(user.RoleID)//dosomethigyouwatwithuser/role})err:=cc.Resolve(fuc(userReporepo.UserRepo,roleReporepo.RoleRepoo)error{user,err:=userRepo.GetUser(123)iferr!=il{returerr}role,err:=roleRepo.GetRole(user.RoleID)iferr!=il{returerr}//dosomethigyouwatwithuser/rolereturil})iferr!=il{//自定义错误处理}CallCall(callbackiterface{})([]iterface{},error) 方法不仅完成对象的依赖注入,还会返回 callback 的返回值,返回值为数组结构。比如results,err:=cc.Call(fuc(userReporepo.UserRepo)([]repo.User,error){users,err:=userRepo.AllUsers()returusers,err})iferr!=il{//这里的err是依赖注入过程中的错误,比如依赖对象创建失败}//results是一个类型为[]iterface{}的数组,数组中按次序包含了callback函数的返回值//results[0]-[]repo.User//results[1]-error//由于每个返回值都是iterface{}类型,因此在使用时需要执行类型断言,将其转换为具体的类型再使用users:=results[0].([]repo.User)err:=results[0].(error)Provider有时我们希望为不同的功能模块绑定不同的对象实现,比如在Web服务器中,每个请求的hadler函数需要访问与本次请求有关的request/respose对象,请求结束之后,Cotaier 中的request/respose对象也就没有用了,不同的请求获取到的也不是同一个对象。我们可以使用 CallWithProvider(callbackiterface{},providerfuc()[]*Etity)([]iterface{},error) 配合 Provider(iitializes...iterface{})(fuc()[]*Etity,error) 方法实现该功能。ctxFuc:=fuc()Cotext{returctx}requestFuc:=fuc()Request{returctx.request}provider,_:=cc.Provider(ctxFuc,requestFuc)results,err:=cc.CallWithProvider(fuc(userReporepo.UserRepo,reqRequest)([]repo.User,error){//这里我们注入的Request对象,只对当前callback有效userId:=req.Iput("user_id")users,err:=userRepo.GetUser(userId)returusers,err},provider)AutoWire结构体属性注入使用 AutoWire 方法可以为结构体的属性注入其绑定的对象,要使用该特性,我们需要在需要依赖注入的结构体对象上添加 autowire 标签。typeUserMaagerstruct{UserRepo*UserRepo`autowire:"@"jso:"-"`field1strig`autowire:"versio"`Field2strig`jso:"field2"`}maager:=UserMaager{}//对maager执行AutoWire之后,会自动注入UserRepo和field1的值iferr:=c.AutoWire(&maager);err!=il{t.Error("testfailed")}结构体属性注入支持公开和私有字段的注入。如果对象是通过类型来注入的,使用 autowire:"@" 来标记属性;如果使用的是 BidValue 绑定的字符串为key的对象,则使用 autowire:"Key名称" 来标记属性。由于 AutoWire 要修改对象,因此必须使用对象的指针,结构体类型必须使用 & 。
其它方法HasBoud/HasBoudValue方法签名HasBoud(keyiterface{})boolHasBoudValue(keystrig)bool用于判断指定的Key是否已经绑定过了。Keys方法签名Keys()[]iterface{}获取所有绑定到 Cotaier 中的对象信息。CaOverride方法签名CaOverride(keyiterface{})(bool,error)判断指定的Key是否可以覆盖,重新绑定创建函数。WithCoditioWithCoditio 并不是 Cotaier 实例的一个方法,而是一个工具函数,用于创建 Coditioal 接口。实现 Coditioal 接口后,在创建实例方法时会根据指定条件是否为true来判断当前实例方法是否有效。WithCoditio(iititerface{},oCoditioiterface{})Coditioal参数 iit 是传递给 Sigleto 和 Prototype 方法的实例创建方法,oCoditio 参数则是一个条件,在调用 Sigleto 及 Prototype 方法时,会执行 oCoditio 函数,该函数支持两种形式oCoditio(依赖注入参数列表...)booloCoditio(依赖注入参数列表...)(bool,error)oCoditio 函数的bool返回值用于控制该实例方法是否生效。ExtedExted 并不是 Cotaier 实例上的一个方法,而是一个独立的函数,用于从已有的Cotaier生成一个新的Cotaier,新的Cotaier继承已有Cotaier所有的对象绑定。Exted(cCotaier)Cotaier容器继承之后,在依赖注入对象查找时,会优先从当前Cotaier中查找,当找不到对象时,再从父对象查找。在Cotaier实例上个,有一个名为 ExtedFrom(paretCotaier) 的方法,该方法用于指定当前Cotaier从paret继承。
示例项目简单的示例可以参考项目的 example 目录。以下项目中使用了 Cotaier 作为依赖注入管理库,感兴趣的可以参考一下。Glacier 一个应用管理框架,目前还没有写使用文档,该框架集成了 Cotaier,用来管理框架的对象实例化。Adaos-Alert 使用Glacier开发的一款报警系统,它侧重点并不是监控,而是报警,可以对各种报警信息进行聚合,按照配置规则来实现多样化的报警,一般用于配合 Logstash 来完成业务和错误日志的报警,配合Prometheus,OpeFalco 等主流监控框架完成服务级的报警。目前还在开发中,但基本功能已经可用。Syc 使用Glacier开发一款跨主机文件同步工具,拥有友好的web配置界面,使用GRPC实现不同服务器之间文件的同步。
评论