We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Goner是Gone框架定义的组件,是只嵌入了gone.Flag的结构体指针,可以用于Gone框架的依赖注入,如下代码就定义了一个简单的Goner:
gone.Flag
package demo import "github.com/gone-io/gone" type Component struct { gone.Flag } var aGoner = &Component{}
Gone框架核心是一个Goner仓库,加载器的作用就是将用户定义的组件(Goner)注册(或者加载,在本文后续我们都称为加载)到仓库中,以便后续的依赖注入。
package main import "github.com/gone-io/gone" func main() { type Dep struct { gone.Flag Name string } // 加载一个Dep gone.Load(&Dep{}) //加载 一个名为dep1的Dep gone.Load(&Dep{}, gone.Name("dep1")) //支持链式调用 gone. Load(&Dep{}, gone.Name("dep3")). Load(&Dep{}, gone.Name("dep2")) //通过加载函数批量加载 gone.Loads(func(loader gone.Loader) error { err := loader.Load(&Dep{}, gone.Name("dep4")) if err != nil { return gone.ToError(err) } err = loader.Load(&Dep{}, gone.Name("dep5")) return err }) }
上面的代码展示了Gone框架中加载Goner的几种方式:
直接加载:使用gone.Load()方法可以直接加载一个Goner。例如gone.Load(&Dep{})将加载一个默认的Dep组件。
gone.Load()
gone.Load(&Dep{})
命名加载:通过gone.Name()选项可以为加载的Goner指定一个名称。例如gone.Load(&Dep{}, gone.Name("dep1"))将加载一个名为"dep1"的Dep组件。
gone.Name()
gone.Load(&Dep{}, gone.Name("dep1"))
链式加载:Gone框架支持链式调用方式加载多个Goner。可以通过.Load()方法连续加载多个组件,使代码更加简洁。
.Load()
批量加载:使用gone.Loads()方法可以在一个函数中批量加载多个Goner。这种方式特别适合需要进行错误处理的场景,可以统一处理加载过程中的错误。
gone.Loads()
这些加载方式提供了灵活的组件注册机制,开发者可以根据具体需求选择合适的方式来加载Goner。
查看源代码,可以看到gone.Loads()方法的定义:
func (s *Application) Loads(loads ...LoadFunc) *Application { //... }
LoadFunc是一个函数类型,定义如下:
LoadFunc
type LoadFunc func (Loader) error
如果编写一个功能模块,需要加载多个Goner到Gone框架中,可以提供一个LoadFunc函数,业务代码只需要通过gone.Loads() 方法调用这个函数即可。
查到源代码,可以看到gone.Load()方法的定义:
func Load(goner Goner, options ...Option) *Application { //... }
Option就是加载选项,它的作用是在加载Goner时,可以设置一些选项,比如gone.Name("dep1")就是设置Goner的名称为"dep1"。
Option
gone.Name("dep1")
支持的选项:
gone.IsDefault(objPointers ...any): 将组件标记为其类型的默认实现。当存在多个相同类型的组件时,如果没有指定具体名称,将使用默认实现进行注入。
gone.IsDefault(objPointers ...any)
// 将EnvConfigure标记为默认实现 gone.Load(&EnvConfigure{}, gone.IsDefault())
gone.Order(order int): 设置组件的启动顺序。order值越小,组件启动越早。框架提供了三个预设的顺序选项:
gone.Order(order int)
gone.HighStartPriority()
Order(-100)
gone.MediumStartPriority()
Order(0)
gone.LowStartPriority()
Order(100)
// Database会在Service之前启动 gone.Load(&Database{}, gone.Order(1)) // 先启动 gone.Load(&Service{}, gone.Order(2)) // 后启动
gone.Name(name string): 为组件设置一个自定义名称。组件可以通过这个名称被注入。
gone.Name(name string)
// 加载一个名为"configure"的组件 gone.Load(&EnvConfigure{}, gone.Name("configure"))
gone.OnlyForName(): 标记组件仅支持基于名称的注入。使用此选项时,组件不会被注册为类型提供者,只能通过显式引用其名称进行注入。
gone.OnlyForName()
// EnvConfigure只能通过`gone:"configure"`标签注入 gone.Load(&EnvConfigure{}, gone.Name("configure"), gone.OnlyForName())
gone.ForceReplace(): 允许替换具有相同名称或类型的现有组件。加载带有此选项的组件时:
gone.ForceReplace()
// 这将替换任何名为"service"的现有组件 gone.Load(&MyService{}, gone.Name("service"), gone.ForceReplace())
gone.LazyFill(): 将组件标记为延迟填充。使用此选项时,组件只有在实际被注入时才会被加载。这对于加载成本高或有外部依赖的组件很有用。
gone.LazyFill()
// 只有在实际注入时才会加载组件 gone.Load(&MyService{}, gone.Name("service"), gone.LazyFill())
package main import "github.com/gone-io/gone" type Dep struct { gone.Flag Name string } type Service struct { gone.Flag dep1 *Dep `gone:""` // 默认注入 dep2 *Dep `gone:"*"` }
在上面的代码中展示了基于类型的注入方式: 按类型注入,需要在被注入的的字段上添加gone:""(或者 gone:"*",在v2中两者是等效的) 标签,框架会自动根据类型进行注入。如果在注入前,加载了多个相同类型的组件,那么框架会优先选择默认实现(通过IsDefault() 选项设置),否则会选择第一个加载的组件并提示警告。
gone:""
gone:"*"
IsDefault()
gone标签中可以指定组件名称,框架会根据名称进行注入。
package main import "github.com/gone-io/gone" type Dep struct { gone.Flag Name string } type Service struct { gone.Flag dep1 *Dep `gone:"dep1"` // 指定名称注入 dep2 *Dep `gone:"dep2"` // 指定名称注入 }
Goner支持两种方式设置名称:
NamedGoner
GonerName
type NamedGoner interface { Goner GonerName() string }
在v1中,给组件注入配置参数是这样写的:
type Service struct { gone.Flag confStr string `gone:"config,configKeyName"` // 通过标签注入配置参数 }
在v2中,依然支持这样方式,底层实现改为由Provider提供值。 让我们来看看Provider的接口定义:
type Provider[T any] interface { Goner Provide(tagConf string) (T, error) }
它是一个泛型接口,在实际使用中,我们需要定义一个Provider,并实现Provide方法,在Provide 方法中,我们可以通过tagConf获取到配置参数,然后返回值即可。必然为了支持上面的confStr的配置,我们需要定义一个Provider,如下:
Provide
confStr
type ConfigProvider struct { gone.Flag } func (c *ConfigProvider) Provide(tagConf string) (string, error) { return config.Get(tagConf) }
在Provide方法中,我们通过tagConf获取到配置参数,然后返回值即可。这样,我们就可以通过Provider给组件注入配置参数了。
tagConf
如果需要被配置的字段是一个int类型,那么我们需要定义一个Provider,如下:
type ConfigProvider struct { gone.Flag } func (c *ConfigProvider) Provide(tagConf string) (int, error) { return config.Get(tagConf) }
会很快发现一个问题,为了实现一个Config模块,我们需要定义无数个Provider,这显然是不合理的,因此,我们需要一个通用的Provider,如下:
type NamedProvider interface { NamedGoner Provide(tagConf string, t reflect.Type) (any, error) }
NamedProvider 接口定义了一个Provide 方法,该方法接收两个参数,第一个参数是tagConf,第二个参数是t,t是reflect.Type类型,用于获取字段的类型,然后根据字段的类型返回对应的值。这样,我们就可以通过NamedProvider给组件注入配置参数了。
现在对比一下 Provider 和 NamedProvider 的区别:
下面讲讲依赖注入查找的流程:
*
Provider[T any]
NoneParamProvider[T any]
NamedProvider
Provide(tagConf string, t reflect.Type) (any, error)
StructFieldInjector
补充说明: NoneParamProvider的定义: type NoneParamProvider[T any] interface { Goner Provide() T } 它是一个泛型接口,它只有一个方法Provide,这个方法没有参数,返回一个值。它的应用场景是,需要通过一个Provider给组件注入一个值,这个值不需要通过参数传递,只需要通过方法返回即可。
补充说明: NoneParamProvider的定义:
type NoneParamProvider[T any] interface { Goner Provide() T }
它是一个泛型接口,它只有一个方法Provide,这个方法没有参数,返回一个值。它的应用场景是,需要通过一个Provider给组件注入一个值,这个值不需要通过参数传递,只需要通过方法返回即可。
StructFieldInjector的定义: type StructFieldInjector interface { NamedGoner Inject(tagConf string, field reflect.StructField, fieldValue reflect.Value) error } 它是一个接口,它只有一个方法InjectStructField,这个方法接收三个参数,第一个参数是v,第二个参数是tagConf,第三个参数是t,t是reflect.Type类型,用于获取字段的类型,然后根据字段的类型返回对应的值。它的应用场景是,需要通过一个Provider给组件注入多个类型的值。
StructFieldInjector的定义:
type StructFieldInjector interface { NamedGoner Inject(tagConf string, field reflect.StructField, fieldValue reflect.Value) error }
它是一个接口,它只有一个方法InjectStructField,这个方法接收三个参数,第一个参数是v,第二个参数是tagConf,第三个参数是t,t是reflect.Type类型,用于获取字段的类型,然后根据字段的类型返回对应的值。它的应用场景是,需要通过一个Provider给组件注入多个类型的值。
InjectStructField
从接口的定义上可以看到,Provider、NoneParamProvider、NamedProvider和StructFieldInjector都是Goner的子接口,要实现他们都必须嵌入 gone.Flag。他们的用途都是将第三方的值提给Gone框架,让框架来进行依赖注入。
BeforeInit() error
BeforeInit()
Init() error
Init()
Daemon
Start() error
gone.Order()
Stop() error
Stop
Start
注意:如果需要Daemon持续提供服务,需要调用Serve()方法,而不是Run(),Serve函数会阻塞,直到End被调用或者进程收到终止的信号。
Serve()
Run()
End
package use_case import ( "github.com/gone-io/gone" "testing" "time" ) type testDaemon struct { gone.Flag isStart bool isStop bool } func (t *testDaemon) Start() error { println("testDaemon Start") t.isStart = true return nil } func (t *testDaemon) Stop() error { println("testDaemon Stop") t.isStop = true return nil } func TestServe(t *testing.T) { daemon := &testDaemon{} var t1, t2 time.Time ins := gone.NewApp() //这里创建了一个实例,在后面的##Application章节有说明。 ins. Load(daemon). BeforeStart(func() { go func() { time.Sleep(5 * time.Millisecond) t1 = time.Now() ins.End() //如果调用gone.End()方法,将终止框架默认的实例`gone.Default` }() }). AfterStop(func() { t2 = time.Now() }). Serve() if !daemon.isStart { t.Fatal("daemon start failed") } if !daemon.isStop { t.Fatal("daemon stop failed") } if !t2.After(t1) { t.Fatal("daemon stop after serve failed") } }
框架提供4个hook函数,分别为作用于beforeStart、afterStart、beforeStop、afterStop。其中两个before Hook 先注册的后执行;两个after Hook 先注册的先执行。下面代码是通过依赖注入的方式来演示的,只能在组件完成初始化之后才能使用。
package use_case import ( "github.com/gone-io/gone" "testing" ) type hookTest struct { gone.Flag beforeStart gone.BeforeStart `gone:""` afterStart gone.AfterStart `gone:""` beforeStop gone.BeforeStop `gone:""` afterStop gone.AfterStop `gone:""` } var orders []int func (h *hookTest) Init() { //通过注入的BeforeStart方法,可以注册一个函数,在服务启动前执行 h.beforeStart(func() { println("before start 1") orders = append(orders, 1) }) //before 类型的hook 可以注册多个,先注册的后执行,后注册的先执行 h.beforeStart(func() { println("before start 2") orders = append(orders, 2) }) h.afterStart(func() { println("after start 3") orders = append(orders, 3) }) h.afterStart(func() { println("after start 4") orders = append(orders, 4) }) h.beforeStop(func() { println("before stop 5") orders = append(orders, 5) }) h.beforeStop(func() { println("before stop 6") orders = append(orders, 6) }) h.afterStop(func() { println("after stop 7") orders = append(orders, 7) }) h.afterStop(func() { println("after stop 8") orders = append(orders, 8) }) } func TestUseHook(t *testing.T) { gone.Load(&hookTest{}).Run() wantedOrder := []int{2, 1, 3, 4, 6, 5, 7, 8} for i := range wantedOrder { if wantedOrder[i] != orders[i] { t.Errorf("wanted %v, got %v", wantedOrder[i], orders[i]) } } }
hook函数也可以直接在加载Goner组件后注册,像这样:
func TestUseHookDirectly(t *testing.T) { type testGoner struct { gone.Flag } gone. Load(&testGoner{}). // 直接注册Hook函数 BeforeStart(func () { println(" BeforeStart") }). AfterStart(func () { println(" AfterStart") }). Run() }
查看源代码,可以发现:gone.Load、gone.Loads、gone.Run、gone.Serve等函数实际是调用的Application的一个实例Default 上的对应方法。
gone.Load
gone.Loads
gone.Run
gone.Serve
Application
Default
var Default = NewApp() //... func Load(goner Goner, options ...Option) *Application { return Default.Load(goner, options...) } //... func Loads(loads ...LoadFunc) *Application { return Default.Loads(loads...) } //...
所以,如果希望在同一个进程中使用多个Gone框架实例,可以使用 gone.NewApp 函数来创建多个 Application 实例,然后分别调用 Run 或Serve 方法启动框架。 下面是NewApp的定义:
gone.NewApp
Run
Serve
NewApp
func NewApp(loads ...LoadFunc) *Application { application := Application{} //.... return &application }
如果希望在组件中,使用代码动态地获取其他组件,可以注入gone.GonerKeeper接口(当然也可以直接注入 *gone.Core),这个接口定义如下:
gone.GonerKeeper
type GonerKeeper interface { GetGonerByName(name string) any GetGonerByType(t reflect.Type) any }
示例代码:
package use_case import ( "github.com/gone-io/gone" "testing" ) type useKeeper struct { gone.Flag keeper gone.GonerKeeper `gone:"*"` core *gone.Core `gone:"*"` } func (u *useKeeper) Test(t *testing.T) { goner := u.keeper.GetGonerByName("*") if goner != u.core { t.Fatal("keeper get core error") } } func TestGonerKeeper(t *testing.T) { gone. NewApp(). Load(&useKeeper{}). Run(func(k *useKeeper) { k.Test(t) }) }
在v2版本中,依然可以使用 接口slice 来接收 多个实例。 测试代码如下:
package use_case import ( "github.com/gone-io/gone" "testing" ) type worker interface { Work() } type workerImpl struct { gone.Flag name string } func (w *workerImpl) Work() { println("worker", w.name, "work") } type workerImpl2 struct { gone.Flag name string } func (w *workerImpl2) Work() { println("worker", w.name, "work") } type factory struct { gone.Flag workers []worker `gone:"*"` } func TestUseSlice(t *testing.T) { gone. NewApp(). Load(&factory{}, gone.Name("factory")). Load(&workerImpl{name: "worker1"}, gone.Name("worker1")). Load(&workerImpl2{name: "worker2"}, gone.Name("worker2")). Run(func(f *factory) { if len(f.workers) != 2 { t.Fatal("worker count is not 2") } }) }
函数参数注入是Gone框架的一个特性,它允许框架在调用函数时,自动从Goner仓库中查找并注入与函数参数类型匹配的组件实例。 在前面例子中,Run方法接收的函数,其参数就是被自动注入的。 例子:
package use_case import ( "github.com/gone-io/gone" "testing" ) type funcTest struct { gone.Flag injector gone.FuncInjector `gone:"*"` } func (f *funcTest) Test(t *testing.T) { // 定义一个需要注入参数的函数 fn := func(factory *factory) { if factory == nil { t.Fatal("factory is nil") } } // 使用 InjectWrapFunc 来执行函数,框架会自动注入参数 wrapped, err := f.injector.InjectWrapFunc(fn, nil, nil) if err != nil { t.Fatal(err) } _ = wrapped() // 也可以注入多个参数 fn2 := func(factory *factory, worker worker) { if factory == nil { t.Fatal("factory is nil") } if worker == nil { t.Fatal("worker is nil") } } wrapped2, err := f.injector.InjectWrapFunc(fn2, nil, nil) if err != nil { t.Fatal(err) } _ = wrapped2() } func TestFuncInjector(t *testing.T) { gone. NewApp(). Load(&funcTest{}). Load(&factory{}, gone.Name("factory")). Load(&workerImpl{name: "worker1"}, gone.Name("worker1")). Run(func(f *funcTest) { f.Test(t) }) }
Config
Logger
在v2版本中,内核代码内置了Config和Logger组件,分别用于配置管理和日志记录。
内置的Config组件,是从环境变量中读取配置,可以通过实现gone.Configure来实现自定义读取配置的方式。 下面结束如何读取配置的示例代码,注意环境变量的名需要加上GONE_前缀,并且需要全部大写,如果被注入的不是简单类型,默认的Configure会尝试使用json解析环境变量的值。
gone.Configure
GONE_
package use_case import ( "github.com/gone-io/gone/v2" "os" "testing" ) type useConfig struct { gone.Flag goneVersion string `gone:"config,gone-version"` //框架在启动时,会自动加载配置,并注入到goRoot字段中。 } func TestUseConfig(t *testing.T) { os.Setenv("GONE_GONE-VERSION", "v2.0.0") gone. Load(&useConfig{}). Run(func(c *useConfig) { println("goRoot:", c.goneVersion) if c.goneVersion != "v2.0.0" { t.Fatal("配置注入失败") } }) }
v2内核内置的Logger,只是简单在控制台打印日志,可以通过实现gone.Logger来实现自定义日志记录的方式。
gone.Logger
package use_case import ( "github.com/gone-io/gone/v2" "testing" ) type worker struct { gone.Flag log gone.Logger `gone:"*"` } func TestUseLogger(t *testing.T) { gone. Load(&worker{}). Run(func(app *app) { app.log.Infof("hello world") app.log.Errorf("hello world") app.log.Warnf("hello world") app.log.Debugf("hello world") }) }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Gone@v2 使用说明
Goner的定义
Goner是Gone框架定义的组件,是只嵌入了
gone.Flag
的结构体指针,可以用于Gone框架的依赖注入,如下代码就定义了一个简单的Goner:Goner加载器
Gone框架核心是一个Goner仓库,加载器的作用就是将用户定义的组件(Goner)注册(或者加载,在本文后续我们都称为加载)到仓库中,以便后续的依赖注入。
加载Goner
上面的代码展示了Gone框架中加载Goner的几种方式:
直接加载:使用
gone.Load()
方法可以直接加载一个Goner。例如gone.Load(&Dep{})
将加载一个默认的Dep组件。命名加载:通过
gone.Name()
选项可以为加载的Goner指定一个名称。例如gone.Load(&Dep{}, gone.Name("dep1"))
将加载一个名为"dep1"的Dep组件。链式加载:Gone框架支持链式调用方式加载多个Goner。可以通过
.Load()
方法连续加载多个组件,使代码更加简洁。批量加载:使用
gone.Loads()
方法可以在一个函数中批量加载多个Goner。这种方式特别适合需要进行错误处理的场景,可以统一处理加载过程中的错误。这些加载方式提供了灵活的组件注册机制,开发者可以根据具体需求选择合适的方式来加载Goner。
加载函数
查看源代码,可以看到
gone.Loads()
方法的定义:LoadFunc
是一个函数类型,定义如下:如果编写一个功能模块,需要加载多个Goner到Gone框架中,可以提供一个
LoadFunc
函数,业务代码只需要通过gone.Loads()
方法调用这个函数即可。
加载选项
查到源代码,可以看到
gone.Load()
方法的定义:Option
就是加载选项,它的作用是在加载Goner时,可以设置一些选项,比如gone.Name("dep1")
就是设置Goner的名称为"dep1"。支持的选项:
gone.IsDefault(objPointers ...any)
: 将组件标记为其类型的默认实现。当存在多个相同类型的组件时,如果没有指定具体名称,将使用默认实现进行注入。gone.Order(order int)
: 设置组件的启动顺序。order值越小,组件启动越早。框架提供了三个预设的顺序选项:gone.HighStartPriority()
: 相当于Order(-100)
,最早启动gone.MediumStartPriority()
: 相当于Order(0)
,默认启动顺序gone.LowStartPriority()
: 相当于Order(100)
,最后启动gone.Name(name string)
: 为组件设置一个自定义名称。组件可以通过这个名称被注入。gone.OnlyForName()
: 标记组件仅支持基于名称的注入。使用此选项时,组件不会被注册为类型提供者,只能通过显式引用其名称进行注入。gone.ForceReplace()
: 允许替换具有相同名称或类型的现有组件。加载带有此选项的组件时:gone.LazyFill()
: 将组件标记为延迟填充。使用此选项时,组件只有在实际被注入时才会被加载。这对于加载成本高或有外部依赖的组件很有用。依赖注入
基于字段类型的注入
在上面的代码中展示了基于类型的注入方式:
按类型注入,需要在被注入的的字段上添加
gone:""
(或者gone:"*"
,在v2中两者是等效的)标签,框架会自动根据类型进行注入。如果在注入前,加载了多个相同类型的组件,那么框架会优先选择默认实现(通过
IsDefault()
选项设置),否则会选择第一个加载的组件并提示警告。
基于Goner名称注入
gone标签中可以指定组件名称,框架会根据名称进行注入。
Goner支持两种方式设置名称:
gone.Name()
选项设置名称。NamedGoner
接口,通过GonerName
方法设置名称,NamedGoner接口定义如下:通过Provider给组件注入依赖
在v1中,给组件注入配置参数是这样写的:
在v2中,依然支持这样方式,底层实现改为由Provider提供值。
让我们来看看Provider的接口定义:
它是一个泛型接口,在实际使用中,我们需要定义一个Provider,并实现
Provide
方法,在Provide
方法中,我们可以通过tagConf获取到配置参数,然后返回值即可。必然为了支持上面的
confStr
的配置,我们需要定义一个Provider,如下:在
Provide
方法中,我们通过tagConf
获取到配置参数,然后返回值即可。这样,我们就可以通过Provider给组件注入配置参数了。如果需要被配置的字段是一个int类型,那么我们需要定义一个Provider,如下:
会很快发现一个问题,为了实现一个Config模块,我们需要定义无数个Provider,这显然是不合理的,因此,我们需要一个通用的Provider,如下:
NamedProvider 接口定义了一个
Provide
方法,该方法接收两个参数,第一个参数是tagConf,第二个参数是t,t是reflect.Type类型,用于获取字段的类型,然后根据字段的类型返回对应的值。这样,我们就可以通过NamedProvider给组件注入配置参数了。
现在对比一下 Provider 和 NamedProvider 的区别:
下面讲讲依赖注入查找的流程:
*
,那么框架会使用内核中提供Provider来给组件注入值(对,内核其实就是一个Provider,v2的整个注入机制都是基于Provider的)。Provide
方法,将值注入到组件中。Provider[T any]
和NoneParamProvider[T any]
,如果找到了,那么就会调用Provider的Provide
方法,如果他们提供的值是兼容的,那么就会将值注入到组件中。
NamedProvider
,如果找到了,那么就会调用它的Provide(tagConf string, t reflect.Type) (any, error)
方法,它如果能返回一个兼容的值,那么就会将值注入到组件中。StructFieldInjector
来注入值。从接口的定义上可以看到,Provider、NoneParamProvider、NamedProvider和StructFieldInjector都是Goner的子接口,要实现他们都必须嵌入
gone.Flag
。他们的用途都是将第三方的值提给Gone框架,让框架来进行依赖注入。Goner的生命周期
依赖注入前,如果组件上存在
BeforeInit() error
或者BeforeInit()
,那么就会调用这个方法,这个方法会在依赖注入之前被调用,在这个方法中不能够使用依赖注入的值。
依赖注入后,如果组件上存在
Init() error
或者Init()
,那么就会调用这个方法,这个方法会在依赖注入之后被调用,*在这个方法中可以使用依赖注入的值*。
组件被注入到其他组件前,必须先完成自己的初始化。
这个阶段,如果组件实现了
Daemon
接口,该阶段会运行Daemon
接口的Start() error
方法来启动自己。可以在加载组件时,通过gone.Order()
方法设置组件的启动顺序。这个阶段,如果组件实现了
Daemon
接口,该阶段会运行Daemon
接口的Stop() error
方法来停止自己。Stop
的顺序和Start
的顺序相反。
注意:如果需要Daemon持续提供服务,需要调用
Serve()
方法,而不是Run()
,Serve函数会阻塞,直到End
被调用或者进程收到终止的信号。四个hook函数
框架提供4个hook函数,分别为作用于beforeStart、afterStart、beforeStop、afterStop。其中两个before Hook 先注册的后执行;两个after
Hook 先注册的先执行。下面代码是通过依赖注入的方式来演示的,只能在组件完成初始化之后才能使用。
hook函数也可以直接在加载Goner组件后注册,像这样:
Application
查看源代码,可以发现:
gone.Load
、gone.Loads
、gone.Run
、gone.Serve
等函数实际是调用的Application
的一个实例Default
上的对应方法。
所以,如果希望在同一个进程中使用多个Gone框架实例,可以使用
gone.NewApp
函数来创建多个Application
实例,然后分别调用Run
或Serve
方法启动框架。下面是
NewApp
的定义:GonerKeeper
如果希望在组件中,使用代码动态地获取其他组件,可以注入
gone.GonerKeeper
接口(当然也可以直接注入 *gone.Core),这个接口定义如下:示例代码:
数组注入
在v2版本中,依然可以使用 接口slice 来接收 多个实例。
测试代码如下:
使用FuncInjector来实现函数参数的注入
函数参数注入是Gone框架的一个特性,它允许框架在调用函数时,自动从Goner仓库中查找并注入与函数参数类型匹配的组件实例。
在前面例子中,Run方法接收的函数,其参数就是被自动注入的。
例子:
内置的
Config
和Logger
在v2版本中,内核代码内置了
Config
和Logger
组件,分别用于配置管理和日志记录。Config
内置的
Config
组件,是从环境变量中读取配置,可以通过实现gone.Configure
来实现自定义读取配置的方式。下面结束如何读取配置的示例代码,注意环境变量的名需要加上
GONE_
前缀,并且需要全部大写,如果被注入的不是简单类型,默认的Configure会尝试使用json解析环境变量的值。Logger
v2内核内置的Logger,只是简单在控制台打印日志,可以通过实现
gone.Logger
来实现自定义日志记录的方式。The text was updated successfully, but these errors were encountered: