마지막 릴리스 후 불과 2주 만에 Go 백엔드를 위한 원스톱 개발 프레임워크인 Go-Spring이 새 버전을 출시했습니다. 새 버전은 동적 구성과 빈 공유라는 두 가지 매우 중요한 기능을 구현합니다.
동적 구성
때때로 우리는 "동적 구성"이라고 하는 다운타임 없이 프로그램의 구성을 수정하고 프로그램의 동작을 변경할 수 있기를 원합니다. 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는 첫 페이지에서 매우 특별한 기능을 사용합니다. 이 기능은 빈이 공유되는 것처럼 보이는 다른 객체에 한 빈의 필드 값을 주입할 수 있습니다. 이제 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())
}