Only two weeks after the last release, Go-Spring, the one-stop development framework for Go backend, has released a new version. The new version implements two very important features: dynamic configuration and bean sharing.

Dynamic configuration

Sometimes we want to be able to modify the configuration of the program and change the behavior of the program without downtime, the so-called “dynamic configuration”. Go-Spring implements the same usage as ordinary properties by using special data types. It supports both default values ​​and type verification, while also ensuring data concurrency safety, which is very simple and powerful.


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 sharing

Java Spring Redis uses a very special feature on the front page, which can inject the field value of one bean into another object, which looks like the bean is shared. Now Go-Spring can also support such usage.


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())
}

#Onestop #development #framework #GoSpring #released #v112 #version #News Fast Delivery

Leave a Comment

Your email address will not be published. Required fields are marked *