Deux semaines seulement après la dernière version, Go-Spring, le cadre de développement à guichet unique pour le backend Go, a publié une nouvelle version.La nouvelle version implémente deux fonctionnalités très importantes : la configuration dynamique et le partage de bean.
Paramétrage dynamique
Parfois, nous voulons pouvoir modifier la configuration du programme et changer le comportement du programme sans temps d'arrêt, ce que l'on appelle la "configuration dynamique". Go-Spring implémente la même utilisation que les propriétés ordinaires en utilisant des types de données spéciaux. Il prend en charge à la fois les valeurs par défaut et la vérification de type, tout en garantissant la sécurité de la concurrence des données, ce qui est très simple et puissant.
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":{}}}`)
}
}
Partage de haricots
Java Spring Redis utilise une fonctionnalité très spéciale sur la page d'accueil, qui peut injecter la valeur de champ d'un bean dans un autre objet, qui ressemble au bean partagé. Désormais, Go-Spring peut également prendre en charge une telle utilisation.
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())
}