# 服务提供者
# 指南
hade框架使用ServiceProvider机制来满足协议,通过service Provder提供某个协议服务的具体实现。这样如果开发者对具体的实现协议的服务类的具体实现不满意,则可以很方便的通过切换具体协议的Service Provider来进行具体服务的切换。
一个ServiceProvider是一个单独的文件夹,它包含服务提供和服务实现。具体可以参考framework/provider/demo
一个SerivceProvider就是一个独立的包,这个包可以作为插件独立地发布和分享。
你也可以定义一个无contract的ServiceProvider,其中的Name()需要保证唯一。
# 创建
我们可以使用命令 ./hade provider new
来创建一个新的service provider
[~/Documents/workspace/hade_workspace/demo5]$ ./hade provider new
create a provider
? please input provider name test
? please input provider folder(default: provider name):
create provider success, folder path: /Users/Documents/workspace/hade_workspace/demo5/app/provider/test
please remember add provider to kernel
该命令会在app/provider/
目录下创建一个对应的服务提供者。并且初始化好三个文件: contract.go
, provider.go
, service.go
# 自定义
我们需要编写这三个文件:
# contract.go
contract.go 定义了这个服务提供方提供的协议接口。hade 框架任务,作为一个业务的服务提供者,定义一个好的协议是最重要的事情。
所以 contract.go 中定义了一个 Service 接口,在其中定义各种方法,包含输入参数和返回参数。
package demo
const DemoKey = "demo"
type IService interface {
GetAllStudent() []Student
}
type Student struct {
ID int
Name string
}
其中还定义了一个Key, 这个 Key 是全应用唯一的,服务提供者将服务以 Key 关键字注入到容器中。服务使用者使用 Key 关键字获取服务。
# provider
provider.go 提供服务适配的实现,实现一个Provider必须实现对应的五个方法
package demo
import (
"github.com/gohade/hade/framework"
)
type DemoProvider struct {
framework.ServiceProvider
c framework.Container
}
func (sp *DemoProvider) Name() string {
return DemoKey
}
func (sp *DemoProvider) Register(c framework.Container) framework.NewInstance {
return NewService
}
func (sp *DemoProvider) IsDefer() bool {
return false
}
func (sp *DemoProvider) Params() []interface{} {
return []interface{}{sp.c}
}
func (sp *DemoProvider) Boot(c framework.Container) error {
sp.c = c
return nil
}
- Name() // 指定这个服务提供者提供的服务对应的接口的关键字
- Register() // 这个服务提供者注册的时候调用的方法,一般是指定初始化服务的函数名
- IsDefer() // 这个服务是否是使用时候再进行初始化,false为注册的时候直接进行初始化服务
- Params() // 初始化服务的时候对服务注入什么参数,一般把 container 注入到服务中
- Boot() // 初始化之前调用的函数,一般设置一些全局的Provider
# service.go
service.go提供具体的实现,它至少需要提供一个实例化的方法 NewService(params ...interface{}) (interface{}, error)
。
package demo
import "github.com/gohade/hade/framework"
type Service struct {
container framework.Container
}
func NewService(params ...interface{}) (interface{}, error) {
container := params[0].(framework.Container)
return &Service{container: container}, nil
}
func (s *Service) GetAllStudent() []Student {
return []Student{
{
ID: 1,
Name: "foo",
},
{
ID: 2,
Name: "bar",
},
}
}
# 注入
hade的路由,controller的定义是选择基于gin框架进行扩展的。所有的gin框架的路由,参数获取,验证,context都和gin框架是相同的。唯一不同的是gin的全局路由gin.Engine实现了hade的容器结构,可以对gin.Engine进行服务提供的注入,且可以从context中获取具体的服务。
hade提供两种服务注入的方法:
- Bind: 将一个ServiceProvider绑定到容器中,可以控制其是否是单例
- Singleton: 将一个单例ServiceProvider绑定到容器中
建议在文件夹 app/provider/kernel.go
中进行服务注入
func RegisterCustomProvider(c framework.Container) {
c.Bind(&demo.DemoProvider{}, true)
}
当然你也可以在某个业务模块路由注册的时候进行服务注入
func Register(r *gin.Engine) error {
api := NewDemoApi()
r.Container().Singleton(&demoService.DemoProvider{})
r.GET("/demo/demo", api.Demo)
r.GET("/demo/demo2", api.Demo2)
return nil
}
# 获取
hade提供了三种服务获取的方法:
- Make: 根据一个Key获取服务,获取不到获取报错
- MustMake: 根据一个Key获取服务,获取不到返回空
- MakeNew: 根据一个Key获取服务,每次获取都实例化,对应的ServiceProvider必须是以非单例形式注入
你可以在任意一个可以获取到 container 的地方进行服务的获取。
业务模块中:
func (api *DemoApi) Demo2(c *gin.Context) {
demoProvider := c.MustMake(demoService.DemoKey).(demoService.IService)
students := demoProvider.GetAllStudent()
usersDTO := StudentsToUserDTOs(students)
c.JSON(200, usersDTO)
}
命令行中:
var CenterCommand = &cobra.Command{
Use: "direct_center",
Short: "计算区域中心点",
RunE: func(c *cobra.Command, args []string) error {
container := util.GetContainer(c.Root())
app := container.MustMake(contract.AppKey).(contract.App)
return nil
}
甚至于另外一个服务提供者中:
type Service struct {
c framework.Container
baseURL string
userID string
token string
logger contract.Log
}
func NewService(params ...interface{}) (interface{}, error) {
c := params[0].(framework.Container)
config := c.MustMake(contract.ConfigKey).(contract.Config)
baseURL := config.GetString("app.stsmap.url")
userID := config.GetString("app.stsmap.user_id")
token := config.GetString("app.stsmap.token")
logger := c.MustMake(contract.LogKey).(contract.Log)
return &Service{baseURL: baseURL, logger: logger, userID: userID, token: token}, nil
}
# hade provider
hade 框架默认自带了一些服务提供者,提供基础的服务接口协议,可以通过 ./hade provider list
来获取已经安装的服务提供者。
[~/Documents/workspace/hade_workspace/demo5]$ ./hade provider list
hade:app
hade:env
hade:config
hade:log
hade:ssh
hade:kernel
hade 框架自带的服务提供者的 key 是以 hade:
开头。目的为的是与自定义服务提供者的 key 区别开。
hade 框架自带的服务提供者具体定义的协议可以参考:provider