品牌好的佛山网站建设价格,vi设计公司联系方式,柳州住房和城乡建设厅网站,河南免费网站建设公司推荐服务容器的实现
一个服务容器主要的功能是#xff1a;为服务提供注册绑定、提供获取服务实例#xff0c;所以服务容器至少有两个方法#xff1a;注册方法 Bind、获取实例方法 Make。
对于注册的方法#xff0c;直接将一个服务提供者注册到容器中#xff0c;参数是之前定…服务容器的实现
一个服务容器主要的功能是为服务提供注册绑定、提供获取服务实例所以服务容器至少有两个方法注册方法 Bind、获取实例方法 Make。
对于注册的方法直接将一个服务提供者注册到容器中参数是之前定义的服务提供者返回值则是 error 是否注册成功。
// Bind 绑定一个服务提供者如果关键字凭证已经存在会进行替换操作不返回 error
Bind(provider ServiceProvider) error获取实例的方法是 Make它会根据一个关键字凭证来获取容器中已经实例化好的服务。所以参数是一个关键字凭证 string返回值是实例化好的服务 interface 和是否有错误的 error 信息。
// Make 根据关键字凭证获取一个服务
Make(key string) (interface{}, error)除了这两个再考虑在注册绑定及获取服务实例过程中有什么方面可以扩展。
是否已绑定 IsBind, 参数为关键字凭证返回为 bool 表示是否已经绑定不关心Make方法的error: MustMake, 它的参数和 Make 方法一样为关键字凭证返回值为实例化服务但是不返回 error根据参数获取不同初始化实例 MakeNew // Container 是一个服务容器提供绑定服务和获取服务的功能
type Container interface {// Bind 绑定一个服务提供者如果关键字凭证已经存在会进行替换操作返回 errorBind(provider ServiceProvider) error// IsBind 关键字凭证是否已经绑定服务提供者IsBind(key string) bool// Make 根据关键字凭证获取一个服务Make(key string) (interface{}, error)// MustMake 根据关键字凭证获取一个服务如果这个关键字凭证未绑定服务提供者那么会 panic。// 所以在使用这个接口的时候请保证服务容器已经为这个关键字凭证绑定了服务提供者。MustMake(key string) interface{}// MakeNew 根据关键字凭证获取一个服务只是这个服务并不是单例模式的// 它是根据服务提供者注册的启动函数和传递的 params 参数实例化出来的// 这个函数在需要为不同参数启动不同实例的时候非常有用MakeNew(key string, params []interface{}) (interface{}, error)
}具体实现
在 framework/container.go 中 定义HadeContainer 表示容器的具体实现 // HadeContainer 是服务容器的具体实现
type HadeContainer struct {Container // 强制要求 HadeContainer 实现 Container 接口// providers 存储注册的服务提供者key 为字符串凭证providers map[string]ServiceProvider// instance 存储具体的实例key 为字符串凭证instances map[string]interface{}// lock 用于锁住对容器的变更操作lock sync.RWMutex
}// Bind 将服务容器和关键字做了绑定
func (hade *HadeContainer) Bind(provider ServiceProvider) error {hade.lock.Lock()defer hade.lock.Unlock()key : provider.Name()hade.providers[key] provider// if provider is not deferif provider.IsDefer() false {if err : provider.Boot(hade); err ! nil {return err}// 实例化方法params : provider.Params(hade)method : provider.Register(hade)instance, err : method(params...)if err ! nil {return errors.New(err.Error())}hade.instances[key] instance}return nil
}// Make 方式调用内部的 make 实现
func (hade *HadeContainer) Make(key string) (interface{}, error) {return hade.make(key, nil, false)
}// MakeNew 方式使用内部的 make 初始化
func (hade *HadeContainer) MakeNew(key string, params []interface{}) (interface{}, error) {return hade.make(key, params, true)
}// 真正的实例化一个服务
func (hade *HadeContainer) make(key string, params []interface{}, forceNew bool) (interface{}, error) {hade.lock.RLock()defer hade.lock.RUnlock()// 查询是否已经注册了这个服务提供者如果没有注册则返回错误sp : hade.findServiceProvider(key)if sp nil {return nil, errors.New(contract key have not register)}if forceNew {return hade.newInstance(sp, params)}// 不需要强制重新实例化如果容器中已经实例化了那么就直接使用容器中的实例if ins, ok : hade.instances[key]; ok {return ins, nil}// 容器中还未实例化则进行一次实例化inst, err : hade.newInstance(sp, nil)if err ! nil {return nil, err}hade.instances[key] instreturn inst, nil
}容器和框架的结合
hade 框架中最核心的两个数据结构 Engine 和 Context。
Engine 就是 Core 数据结构这个数据结构是整个框架的入口也承担了整个框架最核心的路由、中间件等部分。Context 对应封装request和response的ctx它为每个请求创建一个 Context其中封装了各种对请求操作的方法。
对应来看我们的服务容器它提供了两类方法绑定操作和获取操作。绑定操作是全局的操作而获取操作是在单个请求中使用的。所以在全局我们为服务容器绑定了服务提供方就能在单个请求中获取这个服务。
那么对应到框架中可以将服务容器存放在 Engine 中并且在 Engine 初始化 Context 的时候将服务容器传递进入 Context。
修改framework/gin/gin.go中Engine 的数据结构 添加container字段 type Engine struct {// 容器container framework.Container...
}func New() *Engine {debugPrintWARNINGNew()engine : Engine{...// 这里注入了 containercontainer: framework.NewHadeContainer(),...}...return engine
}在 Engine 创建 context 的时候, 将容器注入到ctx中 // engine 创建 context
func (engine *Engine) allocateContext() *Context {v : make(Params, 0, engine.maxParams)// 在分配新的 Context 的时候注入了 containerreturn Context{engine: engine, params: v, container: engine.container}
}这样就完成了服务容器的创建和传递接下来完成服务容器方法的封装。
根据上面描述的Engine 中负责绑定Context 中负责获取所以我们将 container 的五个能力拆分到 Engine 和 Context 数据结构中。Engine 封装 Bind 和 IsBind 方法Context 封装 Make、MakeNew、MustMake 方法。
将这些为 Engine 和 Context 增加的新的方法单独存放在一个新的文件 framework/gin/hade_context.go 中。 // engine 实现 container 的绑定封装
func (engine *Engine) Bind(provider framework.ServiceProvider) error {return engine.container.Bind(provider)
}// IsBind 关键字凭证是否已经绑定服务提供者
func (engine *Engine) IsBind(key string) bool {return engine.container.IsBind(key)
}// context 实现 container 的几个封装
// 实现 make 的封装
func (ctx *Context) Make(key string) (interface{}, error) {return ctx.container.Make(key)
}// 实现 mustMake 的封装
func (ctx *Context) MustMake(key string) interface{} {return ctx.container.MustMake(key)
}// 实现 makenew 的封装
func (ctx *Context) MakeNew(key string, params []interface{}) (interface{}, error) {return ctx.container.MakeNew(key, params)
}创建一个服务提供方
下面我们来创建一个服务 DemoService为这个服务创建一个服务提供方 DemoServiceProvider并注入到服务容器中。在业务目录中创建一个目录 provider/demo 存放这个服务。
先搞清楚需要为这个服务设计几个文件。
要有一个服务接口文件 contract.go存放服务的接口文件和服务凭证。需要设计一个 provider.go这个文件存放服务提供方 ServiceProvider 的实现。最后在 service.go 文件中实现具体的服务实例。
如何通过服务提供方创建服务
这里实现非常简单我们需要做两个操作绑定服务提供方、获取服务。
首先是在业务文件夹的 main.go 中绑定操作在 main 函数中完成 engine 的创建之后用在 engine 中封装的 Bind 方法做一次绑定操作。 func main() {// 创建 engine 结构core : gin.New()// 绑定具体的服务core.Bind(demo.DemoServiceProvider{})...
}然后就是服务的获取了。在具体的业务逻辑控制器中我们选择路由 /subject/list/all 对应的控制器 SubjectListController使用为 context 封装的 MustMake 方法来获取 demo 服务实例。
MustMake 的参数为 demo 的服务凭证 demo.Key返回的是一个 interface 结构这个 interface 结构实际上是实现了 demo.Service 接口的一个服务实例。
而在接口的具体输出中输出的是这个接口定义的 GetFoo() 方法的输出也就是最终会从服务容器中获取到 DemoService 的 GetFoo() 方法的返回值 Foo 结构带有字段 Name: “i am foo”输出在页面上。 // 对应路由 /subject/list/all
func SubjectListController(c *gin.Context) {// 获取 demo 服务实例demoService : c.MustMake(demo.Key).(demo.Service)// 调用服务实例的方法foo : demoService.GetFoo()// 输出结果c.ISetOkStatus().IJson(foo)
}最后验证一下在浏览器中我们访问这个路由 /subject/list/all获取到了 Foo 数据结构 Json 化出来的结果。
【小结】
将框架作为一个容器服务注入到框架的engine和ctx中注册时为全局因此用engine使用服务时为请求独自使用因此用ctx来获取服务注意获取服务时是否有并发竞争问题