Skip to content

Commit de78e3d

Browse files
author
dapeng
committed
feat(core): 添加懒加载选项并优化循环依赖检测
- 新增懒加载选项,标记依赖在实际注入时加载 - 优化循环依赖检测逻辑,支持懒加载依赖 - 更新相关测试用例,验证懒加载功能
1 parent 2a7630f commit de78e3d

File tree

5 files changed

+85
-20
lines changed

5 files changed

+85
-20
lines changed

core.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gone
33
import (
44
"fmt"
55
"reflect"
6+
"strings"
67
)
78

89
type actionType int8
@@ -14,8 +15,33 @@ const (
1415
DefaultProviderName = "core-provider"
1516
optionTag = "option"
1617
allowNil = "allowNil"
18+
lazy = "lazy"
1719
)
1820

21+
func filedHasOption(filed *reflect.StructField, tagName string, optionName string) bool {
22+
value, ok := filed.Tag.Lookup(tagName)
23+
if !ok {
24+
return false
25+
}
26+
if value == "" {
27+
return false
28+
}
29+
split := strings.Split(value, ",")
30+
for _, v := range split {
31+
if v == optionName {
32+
return true
33+
}
34+
}
35+
return false
36+
}
37+
38+
func isAllowNilField(filed *reflect.StructField) bool {
39+
return filedHasOption(filed, optionTag, allowNil)
40+
}
41+
func isLazyField(filed *reflect.StructField) bool {
42+
return filedHasOption(filed, optionTag, lazy)
43+
}
44+
1945
// Flag is a marker struct used to identify components that can be managed by the gone framework.
2046
// Embedding this struct in another struct indicates that it can be used with gone's dependency injection.
2147
type Flag struct{}
@@ -283,8 +309,7 @@ func (s *Core) fillOne(coffin *coffin) error {
283309
goneName = DefaultProviderName
284310
}
285311

286-
op, y := field.Tag.Lookup(optionTag)
287-
isAllowNil := y && op == allowNil
312+
isAllowNil := isAllowNilField(&field)
288313

289314
co, err := s.getDepByName(goneName)
290315
if err != nil {

dep.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
// circularDepsError creates an error for circular dependencies
1010
// Parameters:
1111
// - circularDeps: list of coffins involved in circular dependencies
12-
// Returns: error object containing information about the circular dependency components
12+
// Returns: error object containing information about the circular dependency Goners
1313
func circularDepsError(circularDeps []dependency) Error {
1414
var names []string
1515
prefix := "\t"
@@ -203,6 +203,11 @@ func (s *Core) getGonerFillDeps(co *coffin) (fillDependencies []dependency, err
203203
case reflect.Struct:
204204
for i := 0; i < elem.NumField(); i++ {
205205
field := elem.Field(i)
206+
207+
if isLazyField(&field) {
208+
continue
209+
}
210+
206211
if tag, ok := field.Tag.Lookup(goneTag); ok {
207212
gonerName, _ := ParseGoneTag(tag)
208213
if gonerName == "" || gonerName == "*" {
@@ -234,7 +239,7 @@ func (s *Core) getGonerFillDeps(co *coffin) (fillDependencies []dependency, err
234239
} else {
235240
depCo, err := s.getDepByType(field.Type)
236241
if err != nil {
237-
if t, ok := field.Tag.Lookup(optionTag); ok && t == allowNil {
242+
if isAllowNilField(&field) {
238243
continue
239244
}
240245
return nil, ToErrorWithMsg(err, fmt.Sprintf("Cannot find matched value for field %q of %q", field.Name, GetTypeName(elem)))

option.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"reflect"
55
)
66

7-
// Option is an interface for configuring components loaded into the gone framework.
7+
// Option is an interface for configuring Goners loaded into the gone framework.
88
type Option interface {
99
Apply(c *coffin) error
1010
}
@@ -20,8 +20,8 @@ func (o option) Apply(c *coffin) error {
2020
return o.apply(c)
2121
}
2222

23-
// IsDefault returns an Option that marks a component as the default implementation for its type.
24-
// When multiple components of the same type exist, the default one will be used for injection
23+
// IsDefault returns an Option that marks a Goner as the default implementation for its type.
24+
// When multiple Goners of the same type exist, the default one will be used for injection
2525
// if no specific name is requested.
2626
//
2727
// Example usage:
@@ -68,7 +68,7 @@ func IsDefault(objPointers ...any) Option {
6868
}
6969
}
7070

71-
// Order returns an Option that sets the start order for a component.
71+
// Order returns an Option that sets the start order for a Goner.
7272
// Components with lower order values will be started before those with higher values.
7373
// This can be used to control started sequence when specific ordering is required.
7474
//
@@ -100,15 +100,15 @@ func LowStartPriority() Option {
100100
return Order(100)
101101
}
102102

103-
// Name returns an Option that sets a custom name for a component.
103+
// Name returns an Option that sets a custom name for a Goner.
104104
// Components can be looked up by this name when injecting dependencies.
105105
//
106106
// Example usage:
107107
//
108108
// gone.Load(&EnvConfigure{}, gone.GonerName("configure"))
109109
//
110110
// Parameters:
111-
// - name: String identifier to use for this component
111+
// - name: String identifier to use for this Goner
112112
func Name(name string) Option {
113113
return option{
114114
apply: func(c *coffin) error {
@@ -118,8 +118,8 @@ func Name(name string) Option {
118118
}
119119
}
120120

121-
// OnlyForName returns an Option that marks a component as only available for name-based injection.
122-
// When this option is used, the component will not be registered as a type provider,
121+
// OnlyForName returns an Option that marks a Goner as only available for name-based injection.
122+
// When this option is used, the Goner will not be registered as a type provider,
123123
// meaning it can only be injected by explicitly referencing its name.
124124
//
125125
// Example usage:
@@ -136,15 +136,15 @@ func OnlyForName() Option {
136136
}
137137
}
138138

139-
// ForceReplace returns an Option that allows replacing existing components with the same name or type.
140-
// When loading a component with this option:
141-
// - If a component with the same name already exists, it will be replaced
139+
// ForceReplace returns an Option that allows replacing loaded Goners with the same name or type.
140+
// When loading a Goner with this option:
141+
// - If a Goner with the same name already exists, it will be replaced
142142
// - If a provider for the same type already exists, it will be replaced
143143
//
144144
// Example usage:
145145
//
146146
// gone.Load(&MyService{}, gone.GonerName("service"), gone.ForceReplace())
147-
// // This will replace any existing component named "service"
147+
// // This will replace any existing Goner named "service"
148148
func ForceReplace() Option {
149149
return option{
150150
apply: func(c *coffin) error {
@@ -154,14 +154,12 @@ func ForceReplace() Option {
154154
}
155155
}
156156

157-
// LazyFill returns an Option that marks a component as lazy-filled.
158-
// When this option is used, the component will not be loaded until it is actually injected.
159-
// This can be useful for components that are expensive to load or have external dependencies.
157+
// LazyFill returns an Option that marks a Goner as lazy-filled.
158+
// When this option is used, the Goner will be filled at last.
160159
//
161160
// Example usage:
162161
//
163162
// gone.Load(&MyService{}, gone.GonerName("service"), gone.LazyFill())
164-
// // This will load the component only when it is actually injected
165163
func LazyFill() Option {
166164
return option{
167165
apply: func(c *coffin) error {

use_case/circular_dep_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ func TestCircularDependency2(t *testing.T) {
4444
if !strings.Contains(r.(error).Error(), "circular dependency") {
4545
t.Errorf("Expected panic with circular dependency error, got: %v", r)
4646
}
47+
} else {
48+
t.Errorf("Expected panic with circular dependency error, got: %v", r)
4749
}
4850
}()
4951
gone.

use_case/lazy_option_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package use_case
2+
3+
import (
4+
"github.com/gone-io/gone/v2"
5+
"testing"
6+
)
7+
8+
type depA4 struct {
9+
gone.Flag
10+
dep *depB4 `gone:"*"`
11+
}
12+
13+
func (d *depA4) Init() {}
14+
15+
type depB4 struct {
16+
gone.Flag
17+
dep *depA4 `gone:"*" option:"lazy"`
18+
}
19+
20+
func (d *depB4) Init() {}
21+
22+
func TestCircularDependency4(t *testing.T) {
23+
gone.
24+
NewApp().
25+
Load(&depA4{}).
26+
Load(&depB4{}).
27+
Run(func(a4 *depA4, b4 *depB4) {
28+
if a4.dep == nil {
29+
t.Error("a4.dep should be not nil")
30+
}
31+
if b4.dep == nil {
32+
t.Error("b4.dep should be not nil")
33+
}
34+
})
35+
}

0 commit comments

Comments
 (0)