Skip to content

Commit d6726a9

Browse files
committed
Add @configuration supported
1 parent 38f9a57 commit d6726a9

File tree

7 files changed

+166
-12
lines changed

7 files changed

+166
-12
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ In addition to implementing a powerful IoC container similar to Java Spring, Go-
2929
| `@Value` | `value:"${}"` |
3030
| `@Autowired` `@Qualifier` `@Required` | `autowire:"?"` |
3131
| `@Configurable` | `WireBean()` |
32+
| `@Configuration` | `Configuration()` |
3233
| `@Profile` | `ConditionOnProfile()` |
3334
| `@Primary` | `Primary()` |
3435
| `@DependsOn` | `DependsOn()` |

gs/app.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (app *App) Run(resourceLocator ...ResourceLocator) error {
6969

7070
func (app *App) run(resourceLocator ResourceLocator) error {
7171

72-
e := NewConfiguration(resourceLocator)
72+
e := NewAppConfiguration(resourceLocator)
7373

7474
if err := e.Load(app.container.props); nil != err {
7575
return err
@@ -216,7 +216,17 @@ func (app *App) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition {
216216
return app.container.Accept(NewBean(ctor, args...))
217217
}
218218

219+
// Configuration scan that the object `i` has `NewXXX` methods to Ioc container.
220+
func (app *App) Configuration(i interface{}) *BeanDefinition {
221+
return app.container.Configuration(i)
222+
}
223+
219224
// AllowCircularReferences enable circular-references.
220225
func (app *App) AllowCircularReferences() {
221226
app.container.AllowCircularReferences()
222227
}
228+
229+
// Go start a goroutine managed by the IoC container.
230+
func (app *App) Go(fn func(ctx context.Context)) {
231+
app.container.Go(fn)
232+
}

gs/app_configuration.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ import (
2424
"github.com/go-spring-projects/go-spring/conf"
2525
)
2626

27-
type Configuration struct {
27+
type AppConfiguration struct {
2828
resourceLocator ResourceLocator
2929
ActiveProfiles []string `value:"${spring.config.profiles:=}"`
3030
ConfigExtensions []string `value:"${spring.config.extensions:=.properties,.yaml,.yml,.toml,.tml}"`
3131
}
3232

33-
func NewConfiguration(resourceLocator ResourceLocator) *Configuration {
34-
return &Configuration{resourceLocator: resourceLocator}
33+
func NewAppConfiguration(resourceLocator ResourceLocator) *AppConfiguration {
34+
return &AppConfiguration{resourceLocator: resourceLocator}
3535
}
3636

37-
func (e *Configuration) Load(props *conf.Properties) error {
37+
func (e *AppConfiguration) Load(props *conf.Properties) error {
3838
p := conf.New()
3939

4040
if err := loadSystemEnv(p); err != nil {
@@ -62,7 +62,7 @@ func (e *Configuration) Load(props *conf.Properties) error {
6262
return nil
6363
}
6464

65-
func (e *Configuration) loadProperties(props *conf.Properties) error {
65+
func (e *AppConfiguration) loadProperties(props *conf.Properties) error {
6666
var resources []Resource
6767

6868
for _, ext := range e.ConfigExtensions {
@@ -106,7 +106,7 @@ func (e *Configuration) loadProperties(props *conf.Properties) error {
106106
return nil
107107
}
108108

109-
func (e *Configuration) loadResource(filename string) ([]Resource, error) {
109+
func (e *AppConfiguration) loadResource(filename string) ([]Resource, error) {
110110

111111
var locators []ResourceLocator
112112
locators = append(locators, e.resourceLocator)

gs/boot.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,30 @@ func Property(key string, fn interface{}) {
5656

5757
// Accept register bean to Ioc container.
5858
func Accept(b *BeanDefinition) *BeanDefinition {
59-
return bootApp.container.Accept(b)
59+
return bootApp.Accept(b)
6060
}
6161

6262
// Object register bean to Ioc container.
6363
func Object(i interface{}) *BeanDefinition {
64-
return bootApp.container.Accept(NewBean(reflect.ValueOf(i)))
64+
return bootApp.Accept(NewBean(reflect.ValueOf(i)))
6565
}
6666

6767
// Provide register bean to Ioc container.
6868
func Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition {
69-
return bootApp.container.Accept(NewBean(ctor, args...))
69+
return bootApp.Accept(NewBean(ctor, args...))
70+
}
71+
72+
// Configuration scan the object `i` has `NewXXX` methods to Ioc container.
73+
func Configuration(i interface{}) *BeanDefinition {
74+
return bootApp.Configuration(i)
7075
}
7176

7277
// Go start a goroutine managed by the IoC container.
7378
func Go(fn func(ctx context.Context)) {
74-
bootApp.container.Go(fn)
79+
bootApp.Go(fn)
7580
}
7681

7782
// AllowCircularReferences enable circular-references.
7883
func AllowCircularReferences() {
79-
bootApp.container.AllowCircularReferences()
84+
bootApp.AllowCircularReferences()
8085
}

gs/gs.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type Container interface {
5959
AllowCircularReferences()
6060
Object(i interface{}) *BeanDefinition
6161
Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition
62+
Configuration(i interface{}) *BeanDefinition
6263
Refresh() error
6364
Close()
6465
}
@@ -187,6 +188,81 @@ func (c *container) AllowCircularReferences() {
187188
c.allowCircularReferences = true
188189
}
189190

191+
// Configuration scan that the object `i` has `NewXXX` methods to Ioc container.
192+
//
193+
// example:
194+
//
195+
// func(x *T) NewFoo() *Foo
196+
//
197+
// func(x *T) NewBar(foo *Foo) *Bar
198+
//
199+
// func(x *T) NewSubject(bar *Bar) (*Subject, error)
200+
//
201+
// func(x *T) NewServer() *gs.BeanDefinition
202+
func (c *container) Configuration(i interface{}) *BeanDefinition {
203+
204+
bValue := reflect.ValueOf(i)
205+
bType := bValue.Type()
206+
207+
for j := 0; j < bType.NumMethod(); j++ {
208+
method := bType.Method(j)
209+
if !strings.HasPrefix(method.Name, "New") {
210+
continue
211+
}
212+
213+
switch method.Type.NumOut() {
214+
case 1: // func(x *T) NewFoo() *Foo
215+
outType := method.Type.Out(0)
216+
if !utils.IsBeanType(outType) {
217+
continue
218+
}
219+
220+
if outType == bdType {
221+
if method.Type.NumIn() != 1 {
222+
panic(fmt.Errorf("non-parameter constructor required: %s", method.Type.String()))
223+
}
224+
225+
bdValues := method.Func.Call([]reflect.Value{bValue})
226+
bdInst := bdValues[0].Interface()
227+
c.Accept(bdInst.(*BeanDefinition))
228+
} else {
229+
c.Provide(method.Func.Interface())
230+
}
231+
232+
case 2:
233+
out0Type := method.Type.Out(0)
234+
if !utils.IsBeanType(out0Type) {
235+
continue
236+
}
237+
238+
out1Type := method.Type.Out(1)
239+
if !utils.IsErrorType(out1Type) {
240+
panic(fmt.Errorf("%s: second return type must be error", method.Type.String()))
241+
}
242+
243+
if out0Type == bdType {
244+
if method.Type.NumIn() != 1 {
245+
panic(fmt.Errorf("non-parameter constructor required: %s", method.Type.String()))
246+
}
247+
248+
bdValues := method.Func.Call([]reflect.Value{bValue})
249+
bdInst := bdValues[0].Interface()
250+
bdErr := bdValues[1].Interface()
251+
if err, ok := bdErr.(error); ok && nil != err {
252+
panic(fmt.Errorf("%s: %w", method.Type.String(), err))
253+
}
254+
c.Accept(bdInst.(*BeanDefinition))
255+
} else {
256+
c.Provide(method.Func.Interface())
257+
}
258+
}
259+
260+
// ignore other methods
261+
}
262+
263+
return c.Accept(NewBean(bValue))
264+
}
265+
190266
// Dependencies return the dependency order list in either ascending or descending order.
191267
func (c *container) Dependencies(asc bool) (deps []*BeanDefinition) {
192268
if !asc {

gs/gs_bean.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ type BeanDefinition struct {
9898
exports []reflect.Type // 导出的接口
9999
}
100100

101+
// bdType type of *BeanDefinition
102+
var bdType = reflect.TypeOf((*BeanDefinition)(nil))
103+
101104
// Type Return the type of the bean.
102105
func (d *BeanDefinition) Type() reflect.Type {
103106
return d.t

gs/gs_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2949,3 +2949,62 @@ func TestLogLogger(t *testing.T) {
29492949
assert.Matches(t, stdWriter.String(), "json info message")
29502950

29512951
}
2952+
2953+
type testFoo struct {
2954+
prefix string
2955+
}
2956+
2957+
type testBar struct {
2958+
foo *testFoo
2959+
}
2960+
2961+
type testSubject struct {
2962+
Bar *testBar `autowire:""`
2963+
}
2964+
2965+
type testAutoConfiguration struct {
2966+
Prefix string `value:"${prefix}"`
2967+
}
2968+
2969+
func (tac *testAutoConfiguration) MakeXX() *testFoo {
2970+
panic("unreadable code")
2971+
}
2972+
2973+
func (tac *testAutoConfiguration) NewEmpty() *BeanDefinition {
2974+
return NewBean(new(struct{})).Name("empty")
2975+
}
2976+
2977+
func (tac *testAutoConfiguration) NewFoo() *testFoo {
2978+
return &testFoo{prefix: tac.Prefix}
2979+
}
2980+
2981+
func (tac *testAutoConfiguration) NewBar(foo *testFoo) (*testBar, error) {
2982+
return &testBar{foo: foo}, nil
2983+
}
2984+
2985+
func (tac *testAutoConfiguration) NewSubject() (*BeanDefinition, error) {
2986+
return NewBean(new(testSubject)).On(cond.OnProperty("open", cond.HavingValue("true"))), nil
2987+
}
2988+
2989+
type testConfiguration struct {
2990+
Subject *testSubject `autowire:""`
2991+
}
2992+
2993+
func TestConfiguration(t *testing.T) {
2994+
c := New()
2995+
p := conf.New()
2996+
p.Set("prefix", "hello")
2997+
p.Set("open", "true")
2998+
2999+
err := c.Properties().Refresh(p)
3000+
assert.Nil(t, err)
3001+
3002+
c.Configuration(new(testAutoConfiguration))
3003+
bd := c.Object(new(testConfiguration))
3004+
3005+
err = c.Refresh()
3006+
assert.Nil(t, err)
3007+
3008+
subject := bd.Interface().(*testConfiguration)
3009+
assert.Equal(t, subject.Subject.Bar.foo.prefix, "hello")
3010+
}

0 commit comments

Comments
 (0)