Skip to content

Commit f9fdffc

Browse files
authored
Merge pull request #5 from go-spring/develop
Develop
2 parents f6e531d + cf01a22 commit f9fdffc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+13580
-2015
lines changed

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
.DS_Store
22
vendor/
3-
.idea/
3+
.idea/
4+
5+
/conf/remote/app-online.properties
6+
7+
gs/examples/bookman/conf/
8+
gs/examples/bookman/log/*.log
9+
gs/examples/bookman/.cover/covcounters.*
10+
gs/examples/bookman/.cover/covmeta.*
11+
gs/examples/bookman/.cover/cover.txt

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,99 @@
11
# spring-core
2+
3+
# 介绍
4+
5+
Go-Spring 是为 Go 开发者打造的轻量级微服务框架,灵感来源于 Java 的 Spring 和 Spring Boot。
6+
它旨在降低开发门槛、提高项目结构的清晰度和可维护性。主要特点包括:
7+
8+
- 易用性:通过注解标签和链式调用注册 Bean 与配置,降低手写样板代码。
9+
- 扩展性:支持动态刷新属性和 Bean,在运行时调整配置而无需重启应用。
10+
- 微服务支持:内置启动框架及丰富的扩展接口,可快速构建多种微服务应用。
11+
- 测试友好:提供丰富的单元测试工具和 mock 能力,保证代码质量。
12+
13+
# 快速开始
14+
15+
### 安装
16+
17+
通过 Go Modules 获取最新版本:
18+
19+
```
20+
go get github.com/go-spring/spring-core@develop
21+
```
22+
23+
### 最小示例
24+
25+
下面是一个最简单的示例,展示了如何注册一个 Bean、绑定属性、动态属性以及启动应用:
26+
27+
```go
28+
package main
29+
30+
import (
31+
"fmt"
32+
"net/http"
33+
"time"
34+
35+
"github.com/go-spring/spring-core/gs"
36+
"github.com/go-spring/spring-core/util/sysconf"
37+
"github.com/go-spring/spring-core/util/syslog"
38+
)
39+
40+
func init() {
41+
// Register the Service struct as a bean.
42+
gs.Object(&Service{})
43+
44+
// Provide a [*http.ServeMux] as a bean.
45+
gs.Provide(func(s *Service) *http.ServeMux {
46+
http.HandleFunc("/echo", s.Echo)
47+
http.HandleFunc("/refresh", s.Refresh)
48+
return http.DefaultServeMux
49+
})
50+
}
51+
52+
const timeLayout = "2006-01-02 15:04:05.999 -0700 MST"
53+
54+
type Service struct {
55+
StartTime time.Time `value:"${start-time}"`
56+
RefreshTime gs.Dync[time.Time] `value:"${refresh-time}"`
57+
}
58+
59+
func (s *Service) Echo(w http.ResponseWriter, r *http.Request) {
60+
str := fmt.Sprintf("start-time: %s refresh-time: %s",
61+
s.StartTime.Format(timeLayout),
62+
s.RefreshTime.Value().Format(timeLayout))
63+
_, _ = w.Write([]byte(str))
64+
}
65+
66+
func (s *Service) Refresh(w http.ResponseWriter, r *http.Request) {
67+
_ = sysconf.Set("refresh-time", time.Now().Format(timeLayout))
68+
_ = gs.RefreshProperties()
69+
_, _ = w.Write([]byte("OK!"))
70+
}
71+
72+
func main() {
73+
_ = sysconf.Set("start-time", time.Now().Format(timeLayout))
74+
_ = sysconf.Set("refresh-time", time.Now().Format(timeLayout))
75+
76+
// Start the Go-Spring application. If it fails, log the error.
77+
if err := gs.Run(); err != nil {
78+
syslog.Errorf("app run failed: %s", err.Error())
79+
}
80+
}
81+
```
82+
83+
当你运行这个程序时,它将启动一个 HTTP 服务器,并注册两个处理器:一个处理 "/echo" 请求,
84+
返回当前时间和刷新时间;另一个处理 "/refresh" 请求,用于刷新配置并返回 "OK!"。
85+
86+
运行这个程序,你可以访问 "/echo" 和 "/refresh",并观察到它们返回的当前时间和刷新时间。
87+
88+
```shell
89+
~ curl http://127.0.0.1:9090/echo
90+
start-time: 2025-03-14 13:32:51.608 +0800 CST refresh-time: 2025-03-14 13:32:51.608 +0800 CST%
91+
~ curl http://127.0.0.1:9090/refresh
92+
OK!%
93+
~ curl http://127.0.0.1:9090/echo
94+
start-time: 2025-03-14 13:32:51.608 +0800 CST refresh-time: 2025-03-14 13:33:02.936 +0800 CST%
95+
~ curl http://127.0.0.1:9090/refresh
96+
OK!%
97+
~ curl http://127.0.0.1:9090/echo
98+
start-time: 2025-03-14 13:32:51.608 +0800 CST refresh-time: 2025-03-14 13:33:08.88 +0800 CST%
99+
```

conf/bind.go

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ type Filter interface {
123123
}
124124

125125
// BindValue binds properties to a value.
126-
func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) {
126+
func BindValue(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) {
127127

128128
if !util.IsPropBindingTarget(t) {
129129
err := errors.New("target should be value type")
@@ -217,7 +217,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind
217217
}
218218

219219
// bindSlice binds properties to a slice value.
220-
func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
220+
func bindSlice(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
221221

222222
et := t.Elem()
223223
p, err := getSlice(p, et, param)
@@ -250,7 +250,7 @@ func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind
250250
return nil
251251
}
252252

253-
func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyProperties, error) {
253+
func getSlice(p Properties, et reflect.Type, param BindParam) (Properties, error) {
254254

255255
// properties that defined as list.
256256
if p.Has(param.Key + "[0]") {
@@ -308,7 +308,7 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP
308308
}
309309

310310
// bindMap binds properties to a map value.
311-
func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
311+
func bindMap(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
312312

313313
if param.Tag.HasDef && param.Tag.Def != "" {
314314
err := errors.New("map can't have a non-empty default value")
@@ -350,7 +350,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa
350350
}
351351

352352
// bindStruct binds properties to a struct value.
353-
func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
353+
func bindStruct(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
354354

355355
if param.Tag.HasDef && param.Tag.Def != "" {
356356
err := errors.New("struct can't have a non-empty default value")
@@ -399,25 +399,12 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin
399399
}
400400
continue
401401
}
402-
403-
if util.IsPropBindingTarget(ft.Type) {
404-
if subParam.Key == "" {
405-
subParam.Key = ft.Name
406-
} else {
407-
subParam.Key = subParam.Key + "." + ft.Name
408-
}
409-
subParam.Key = strings.ToLower(subParam.Key)
410-
subParam.Key = strings.ReplaceAll(subParam.Key, "_", ".")
411-
if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil {
412-
return err // no wrap
413-
}
414-
}
415402
}
416403
return nil
417404
}
418405

419406
// resolve returns property references processed property value.
420-
func resolve(p ReadOnlyProperties, param BindParam) (string, error) {
407+
func resolve(p Properties, param BindParam) (string, error) {
421408
const defVal = "@@def@@"
422409
val := p.Get(param.Key, defVal)
423410
if val != defVal {
@@ -433,7 +420,7 @@ func resolve(p ReadOnlyProperties, param BindParam) (string, error) {
433420
}
434421

435422
// resolveString returns property references processed string.
436-
func resolveString(p ReadOnlyProperties, s string) (string, error) {
423+
func resolveString(p Properties, s string) (string, error) {
437424

438425
var (
439426
length = len(s)

conf/bind_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func TestProperties_Bind(t *testing.T) {
9292
})
9393

9494
t.Run("simple bind", func(t *testing.T) {
95-
p, err := conf.Load("testdata/config/application.yaml")
95+
p, err := conf.Load("testdata/config/app.yaml")
9696
assert.Nil(t, err)
9797

9898
dbConfig1 := DbConfig{}
@@ -109,7 +109,7 @@ func TestProperties_Bind(t *testing.T) {
109109

110110
t.Run("struct bind with tag", func(t *testing.T) {
111111

112-
p, err := conf.Load("testdata/config/application.yaml")
112+
p, err := conf.Load("testdata/config/app.yaml")
113113
assert.Nil(t, err)
114114

115115
dbConfig := TagNestedDbConfig{}
@@ -121,7 +121,7 @@ func TestProperties_Bind(t *testing.T) {
121121

122122
t.Run("struct bind without tag", func(t *testing.T) {
123123

124-
p, err := conf.Load("testdata/config/application.yaml")
124+
p, err := conf.Load("testdata/config/app.yaml")
125125
assert.Nil(t, err)
126126

127127
dbConfig1 := NestedDbConfig{}
@@ -157,7 +157,7 @@ func TestProperties_Bind(t *testing.T) {
157157

158158
t.Run("simple bind from file", func(t *testing.T) {
159159

160-
p, err := conf.Load("testdata/config/application.yaml")
160+
p, err := conf.Load("testdata/config/app.yaml")
161161
assert.Nil(t, err)
162162

163163
var m map[string]string
@@ -170,7 +170,7 @@ func TestProperties_Bind(t *testing.T) {
170170

171171
t.Run("struct bind from file", func(t *testing.T) {
172172

173-
p, err := conf.Load("testdata/config/application.yaml")
173+
p, err := conf.Load("testdata/config/app.yaml")
174174
assert.Nil(t, err)
175175

176176
var m map[string]NestedDB

0 commit comments

Comments
 (0)