距离上次发版仅两周的时间,Go 后端一站式开发框架 Go-Spring 又发布了新的版本,新版本实现了两个非常重要的特性:动态配置和 Bean 共享。
动态配置
有时候我们想要在不停机的情况下可以修改程序的配置,更改程序的行为,即所谓的“动态配置”。Go-Spring 通过使用专门的数据类型实现了和普通属性一样的使用方式,既支持默认值,也支持类型校验,同时还保证了数据的并发安全,非常简单且强大。
type DynamicConfig struct { Int dync.Int64 `value:"${int:=3}" validate:"$<6"` Float dync.Float64 `value:"${float:=1.2}"` Map dync.Ref `value:"${map:=}"` Slice dync.Ref `value:"${slice:=}"` Event dync.Event `value:"${event}"` } type DynamicConfigWrapper struct { Wrapper DynamicConfig `value:"${wrapper}"` } func TestDynamic(t *testing.T) { var cfg *DynamicConfig wrapper := new(DynamicConfigWrapper) c := gs.New() c.Provide(func() *DynamicConfig { config := new(DynamicConfig) config.Int.OnValidate(func(v int64) error { if v < 3 { return errors.New("should greeter than 3") } return nil }) config.Slice.Init(make([]string, 0)) config.Map.Init(make(map[string]string)) config.Event.OnEvent(func(prop *conf.Properties) error { fmt.Println("event fired.") return nil }) return config }).Init(func(config *DynamicConfig) { cfg = config }) c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { p.Wrapper.Slice.Init(make([]string, 0)) p.Wrapper.Map.Init(make(map[string]string)) p.Wrapper.Event.OnEvent(func(prop *conf.Properties) error { fmt.Println("event fired.") return nil }) }) err := c.Refresh() assert.Nil(t, err) { b, _ := json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) b, _ = json.Marshal(wrapper) assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}}`) } { p := conf.New() p.Set("int", 4) p.Set("float", 2.3) p.Set("map.a", 1) p.Set("map.b", 2) p.Set("slice[0]", 3) p.Set("slice[1]", 4) p.Set("wrapper.int", 3) p.Set("wrapper.float", 1.5) p.Set("wrapper.map.a", 9) p.Set("wrapper.map.b", 8) p.Set("wrapper.slice[0]", 4) p.Set("wrapper.slice[1]", 6) c.Properties().Refresh(p) } { b, _ := json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) b, _ = json.Marshal(wrapper) assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) } }
Bean 共享
Java Spring Redis 在首页使用了一个非常特别的特性,可以将一个 Bean 的字段值注入到另一个对象中,看起来就像是 Bean 被共享了。现在 Go-Spring 也能支持这样的使用方式。
type runner struct { Client *redis.Client `autowire:""` StrOps *redis.StringOperations `autowire:"RedisClient"` } func (r *runner) Run(ctx gs.Context) { _, err := r.Client.OpsForString().Get(ctx.Context(), "nonexisting") if !redis.IsErrNil(err) { panic(errors.New("should be redis.ErrNil")) } _, err = r.Client.OpsForString().Set(ctx.Context(), "mykey", "Hello") util.Panic(err).When(err != nil) v, err := r.Client.OpsForString().Get(ctx.Context(), "mykey") util.Panic(err).When(err != nil) if v != "Hello" { panic(errors.New("should be \"Hello\"")) } v, err = r.StrOps.Get(ctx.Context(), "mykey") util.Panic(err).When(err != nil) if v != "Hello" { panic(errors.New("should be \"Hello\"")) } go gs.ShutDown() } func main() { gs.Object(&runner{}).Export((*gs.AppRunner)(nil)) fmt.Printf("program exited %v\n", gs.Web(false).Run()) }
还没有评论,来说两句吧...