前回のリリースからわずか 2 週間後、Go バックエンドのワンストップ開発フレームワークである Go-Spring が新しいバージョンをリリースしました. 新しいバージョンには、動的構成と Bean 共有という 2 つの非常に重要な機能が実装されています.
動的構成
プログラムの構成を変更し、ダウンタイムなしでプログラムの動作を変更できるようにしたい場合があります。いわゆる「動的構成」です。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":{}}}`)
}
}
豆の共有
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())
}